Защита приложения. Часть 1

Первая часть — простые решения: пароли и привязка к железу
3 минуты9332

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

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

  • Люди, не имеющие ни желания, ни знаний, чтобы даже попытаться обмануть систему и получить доступ к данным;
  • Пользователи с определённой тягой к запретному, теряющие интерес после нескольких неудачных взломов;
  • Максимальный уровень угрозы — вредители-коммунисты, которые уничтожат защиту и поделятся вашими трудами со всем миром.

Против простых пользователей особая защита не нужна

Если ваша целевая аудитория — добродушные пользователи, а программа оценивается не в валюте, а в количестве положительных отзывов, то ломать голову над её защитой ни к чему. Достаточно просто создать приветственную форму с вводом личных данных. Но чтобы избежать случайных недоразумений или просто классово упорядочить клиентов, можно непосредственно в коде прописать пароли с уровнями доступа, используя if или switch. Простой пример:

Console.Write("Please enter your password: ");
string str = Console.ReadLine();
        int passway = 0;
        // Определяем уровень доступа по паролю
        switch (str)
        {
            case "qwerty":
            case "user":
                passway = 1; //допустим, первый – самый низкий уровень доступа
                break;
            case "qazwsx":
            case "operator":
                passway = 2; //данный тип наделяется большими правами
               break;
            case "q3y6e2ccn":
            case "admin":
                passway = 3;//пользователь становится всемогущим
                break;
            default:
                Console.WriteLine("Invalid password. Please try again");
                break;
        }

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

Чтобы избежать такого элементарного взлома, необходимо создавать уникальный пароль или ключ для каждого пользователя. Прописывать новые строчки кода каждый раз, когда новый человек получает доступ к приложению — сомнительное занятие. Проще создать генератор ключа, исходя из локальных данных: имени компьютера, версии операционной системы или других уникальных идентификаторов. Чаще всего используют свойства Environment.MachineName (имя компьютера в сети) или Environment.OSVersion.Version. В закрытой сети, где вы знаете эти данные, контролировать доступ не составит труда. Но если не ограничить пользователей в правах администратора на используемом компьютере, доступ откроется после смены этих данных (возможен вариант, когда они вовсе совпадают на разных машинах).

Есть данные поинтереснее, например идентификаторы «железа» (автор кода edward_freedom):

public static class HWIDGrabber
  {
      private static string getGraphicDevice()  //смотрим данные видеокарты
      {
          ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"root\CIMV2", "SELECT * FROM Win32_VideoController");
          string str = "";
          foreach (ManagementObject obj2 in searcher.Get())
          {
              str = Convert.ToString(obj2["Description"]);
          }
          return str;
      }
      private static string getMotherManufacturer()   //производитель материнской платы
      {
          ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"root\CIMV2", "SELECT * FROM Win32_BaseBoard");
          string str = "";
          foreach (ManagementObject obj2 in searcher.Get())
          {
              str = Convert.ToString(obj2["Manufacturer"]);
          }
          return str;
      }
      private static string getMotherSerial() //номер материнской платы
      {
          ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"root\CIMV2", "SELECT * FROM Win32_BaseBoard");
          string str = "";
          foreach (ManagementObject obj2 in searcher.Get())
          {
              str = Convert.ToString(obj2["SerialNumber"]);
          }
          return str;
      }
      private static string GetHDDStaticSerial() // серийный номер жёсткого диска
      {
          string result = string.Empty;
          ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
          foreach (ManagementObject os in searcher.Get())
          {
              result = os["SerialNumber"].ToString().Replace(" ", "");
              if (result.Length > 30)
              {
                  result = HexToString(result);
                  ReverseDouble(ref result);
              }
              break;
 
          }
          return result;
      }

Быстро и просто.

Простейшие методы не помогут против подготовленных пользователей

Однако вот проблема: за пару минут в Google заинтересованный пользователь найдет декомпилятор и, разобравшись в коде, подберет нужные данные. Поэтому надо действовать хитрее, создавая связи с закрытой от посторонних глаз информацией. Проще всего это сделать, используя связь с закрытой базой данных сгенерированных паролей, расположенной на сервере. Ещё лучше, если локальная программа будет исполнять код генерации или поиска на сервере. Это не гарантирует идеальную защиту, но у многих отпадёт желание тратить время.

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

Единственный вариант — убрать как можно больше исполняемого кода с локального компьютера и создать как можно больше «ловушек». Это могут быть неочевидные перекрёстные ссылки, регулярные запросы данных, сверка созданных баз данных и многое другое, вплоть до отсылки такой информации, как местоположение, дата и время доступа. Хорошим решением может быть также стороннее приложение проверки целостности кода.

Вот только против опытных взломщиков и этого недостаточно 

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

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

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

 А тех, кто определился с тем, что хочет стать программистом, ждет профессия «Веб-разработчик».

советыкодызащитапаролипрограммирование
Нашли ошибку в тексте? Напишите нам.
Спасибо,
что читаете наш блог!