2011-12-23 3 views
3

Я разрабатываю программное обеспечение (для личного использования) с Delphi.Какая схема защиты для моих паролей?

Но у меня есть проблема, это он:

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

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

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

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

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

Благодарим Вас за помогает Извините мой плохой английский

ответ

2

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

+0

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

+0

hm Я действительно не понимаю ваш случай использования. * Пользователи * изменяют свой пароль ... вы хотите защитить файл паролей. Итак, где бы вы хотели получить главный пароль? – Nicolas78

+0

Я использую KeePass, и это схема, которую она использует. Я запускаю программу, открываю файл с кодировкой базы данных, запрашивает мой основной пароль, использует его для дешифрования файла, а затем запоминает этот пароль, пока база данных не будет закрыта. Я могу редактировать базу данных по своему усмотрению, даже изменить основной пароль. Файл базы данных повторно зашифровывается с текущим паролем всякий раз, когда он сохраняется в файл. Пароль шифрует сам файл базы данных, а не сохраненные пароли пользователей. Нет необходимости шифровать их отдельно, когда весь файл зашифрован. –

0

Что вы, вероятно, можете сделать, это использовать односторонний хеш для всех паролей, без необходимости использования мастер-пароля d на всех.

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

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

+0

Я не могу использовать хэши для этих паролей, потому что пользователь должен уметь читать и изменять их, поэтому с хешем это непросто! – djiga4me

+1

Вы могли бы упомянуть, что в вопросе;) – fge

+0

Да, спасибо вам! – djiga4me

15

Я хотел бы предложить повернуть проблему на голову. Ваша учетная запись Windows уже защищена паролем. API Win32 предоставляет механизм, в котором вы можете зашифровать данные Windows с помощью пароля Windows.

Это значит, что эти данные надежно защищены как ваш пароль для Windows; и вам не нужно запоминать второй пароль.

Функция Windows CredWrite и CredRead позволяют хранить и сохранять учетные данные; из которых я просто посчастливилось иметь удобную функцию обертки уже для хранения учетных данных:

function CredWriteGenericCredentials(const Target, Username, Password: WideString): Boolean; 
var 
    PersistType: DWORD; 
    Credentials: CREDENTIALW; 
    hr: DWORD; 
    s: string; 
begin 
    if not CredGetMaxPersistType(CRED_TYPE_GENERIC, {var}PersistType) then 
    begin 
     Result := False; 
     Exit; 
    end; 

    ZeroMemory(@Credentials, SizeOf(Credentials)); 
    Credentials.TargetName := PWideChar(Target); //cannot be longer than CRED_MAX_GENERIC_TARGET_NAME_LENGTH (32767) characters. Recommended format "Company_Target" 
    Credentials.Type_ := CRED_TYPE_GENERIC; 
    Credentials.UserName := PWideChar(Username); 
    Credentials.Persist := PersistType; //CRED_PERSIST_ENTERPRISE; //local machine and roaming 
    Credentials.CredentialBlob := PByte(Password); 
    Credentials.CredentialBlobSize := 2*(Length(Password)); //By convention no trailing null. Cannot be longer than CRED_MAX_CREDENTIAL_BLOB_SIZE (512) bytes 
    Credentials.UserName := PWideChar(Username); 
    Result := CredWriteW(Credentials, 0); 

    if not Result then 
    begin 
     hr := GetLastError; 
     case hr of 
     CredUI.ERROR_NO_SUCH_LOGON_SESSION: s := 'The logon session does not exist or there is no credential set associated with this logon session. Network logon sessions do not have an associated credential set. (ERROR_NO_SUCH_LOGON_SESSION)'; 
     CredUI.ERROR_INVALID_PARAMETER: s := 'Certain fields cannot be changed in an existing credential. This error is returned if a field does not match the value in a protected field of the existing credential. (ERROR_INVALID_PARAMETER)'; 
     CredUI.ERROR_INVALID_FLAGS: s := 'A value that is not valid was specified for the Flags parameter. (ERROR_INVALID_FLAGS)'; 
     ERROR_BAD_USERNAME: s := 'The UserName member of the passed in Credential structure is not valid. For a description of valid user name syntax, see the definition of that member. (ERROR_BAD_USERNAME)'; 
     ERROR_NOT_FOUND: s := 'CRED_PRESERVE_CREDENTIAL_BLOB was specified and there is no existing credential by the same TargetName and Type. (ERROR_NOT_FOUND)'; 
//  SCARD_E_NO_READERS_AVAILABLE: raise Exception.Create('The CRED_TYPE_CERTIFICATE credential being written requires the smart card reader to be available. (SCARD_E_NO_READERS_AVAILABLE)'); 
//  SCARD_E_NO_SMARTCARD: raise Exception.Create('A CRED_TYPE_CERTIFICATE credential being written requires the smart card to be inserted. (SCARD_E_NO_SMARTCARD)'); 
//  SCARD_W_REMOVED_CARD: raise Exception.Create('A CRED_TYPE_CERTIFICATE credential being written requires the smart card to be inserted. (SCARD_W_REMOVED_CARD)'); 
//  SCARD_W_WRONG_CHV: raise Exception.Create('The wrong PIN was supplied for the CRED_TYPE_CERTIFICATE credential being written. (SCARD_W_WRONG_CHV)'); 
     else 
      s := SysErrorMessage(hr)+' (0x'+IntToHex(hr, 8)+')'; 
     end; 
     OutputDebugString(PChar(s)); 
    end; 
end; 

и функция-обертки для чтения учетных данных

function CredReadGenericCredentials(const Target: WideString; var Username, Password: WideString): Boolean; 
var 
    Credential: PCREDENTIALW; 
begin 
    Credential := nil; 
    if CredReadW(Target, CRED_TYPE_GENERIC, 0, Credential) then 
    begin 
     try 
      username := Credential.UserName; 
      password := WideCharToWideString(PWideChar(Credential.CredentialBlob), Credential.CredentialBlobSize); //By convention blobs that contain strings do not have a trailing NULL. 
     finally 
      CredFree(Credential); 
     end; 

     Result := True; 
    end 
    else 
     Result := False; 
end; 

Следует отметить, что CredRead и CredWrite сами являются функциями, развернитесь и используйте CryptProtectData и CryptUnprotectData.

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

Позже вы можете дешифровать blob и могут быть дешифрованы только пользователем, который изначально зашифровал его.

Это позволяет вам мечтать о том, чтобы заставить вас иметь дело с другим паролем, но использует Windows для его защиты.

"MyPassword04" --> CryptProtectData() --> "TXlQYXNzd29yZDA0" 

Вы можете хранить свой зашифрованный пароль везде, где захотите. Затем позже:

"TXlQYXNzd29yZDA0" --> CryptUnprotectData() --> "MyPassword04" 

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

Просто предложение; вы можете рассмотреть и отвергнуть это.


Update

Дополнительные вспомогательные функции.

Преобразование PWideChar в WideString (если есть встроенный (Delphi 5) функция для этого, я никогда не нашел):

function WideCharToWideString(Source: PWideChar; SourceLen: Integer): WideString; 
begin 
    if (SourceLen <= 0) then 
    begin 
     Result := ''; 
     Exit; 
    end; 

    SetLength(Result, SourceLen div 2); 
    Move(Source^, Result[1], SourceLen); 
end; 

Есть разные «прицелы», что you'r разрешено хранить кредитные лимиты в:

  • CRED_PERSIST_NONE: Никакие учетные данные не могут быть сохранены. Это значение будет возвращено, если тип учетных данных не поддерживается или был отключен политикой.
  • CRED_PERSIST_SESSION: Только учетные данные, относящиеся к сеансу, могут быть сохранены.
  • CRED_PERSIST_LOCAL_MACHINE: Учетные данные для конкретного сеанса и компьютера могут быть сохранены. Windows XP: Эти учетные данные не могут быть сохранены для сеансов, в которых профиль не загружен.
  • CRED_PERSIST_ENTERPRISE: Любые учетные данные могут быть сохранены. Windows XP: Эти учетные данные не могут быть сохранены для сеансов, в которых профиль не загружен.

Эта функция возвращает самый высокий поддерживаемый тип сохраняемости для заданного типа учетных данных (например, «общие» credentails). Это необходимо, когда вы звоните CredWrite, что вы не пытаетесь упорствовать его в месте, которое не поддерживается (то есть в области, когда нет домена):

type 
    TCredGetSessionTypes = function(MaximumPersistCount: DWORD; MaximumPersist: LPDWORD): BOOL; stdcall; 
function CredGetMaxPersistType(CredType: DWORD; var MaxCredPersistType: DWORD): Boolean; 
const 
    CRED_TYPE_MAXIMUM = 5; 
var 
    _CredGetSessionTypes: TCredGetSessionTypes; 
    MaximumPersist: array[0..CRED_TYPE_MAXIMUM-1] of DWORD; 
begin 
    _CredGetSessionTypes := GetProcedureAddress(advapi32, 'CredGetSessionTypes'); 

    if Assigned(_CredGetSessionTypes) then 
    begin 
     Result := _CredGetSessionTypes(CRED_TYPE_MAXIMUM, PDWORD(@MaximumPersist[0])); 
     if Result then 
      MaxCredPersistType := MaximumPersist[CredType] 
     else 
      MaxCredPersistType := 0; 
    end 
    else 
    begin 
     SetLastError(ERROR_INVALID_FUNCTION); 
     Result := False; 
     MaxCredPersistType := 0; 
    end; 
end; 

Примечание: Любой код выпущен в общественное достояние. Никакой атрибуции не требуется.

+0

Хорошо, но для меня это немного сложно (да :(), и я не люблю быть зависимым от безопасности Windows и пользователя. Спасибо! – djiga4me

+0

Я думаю, что это здорово. Даже если вы не хотите быть в зависимости от безопасности Windows, когда вы не находитесь в окнах, это легкий выбор, когда вы находитесь в окнах, в Delphi.Вы можете повторно зашифровать свой чистый текст «пароль db», используя, например, другое сильное шифрование при его хранении на веб-сайте, но это было бы здорово для обычного ежедневного хранения. –

+0

@ Ян Бойд. Что произойдет, если файл, зашифрованный старым паролем Windows, будет доступен позже? Если пользователь забывает старый пароль, они в беде? Или Windows предоставляет путь миграции для обновления старых зашифрованных данных, которые могут быть сделаны пользователями без изменений? –

0

Проверьте, как безопасен пароль http://passwordsafe.sourceforge.net/.

  1. Соберите список паролей, которые будут зашифрованы.
  2. Генерируйте два случайных 128-разрядных номера, используя безопасный случайный генератор. Используйте первый ключ HMAC для последующей проверки подлинности и проверки целостности. Используйте второй ключ AES-CBC для шифрования списка паролей для шифрования. Добавьте вывод HMAC в конец зашифрованного списка.
  3. Создайте третье случайное число. Используйте это как соль вместе с паролем для получения ключа ключа шифрования с использованием PBKDF. Используйте ключ шифрования ключа для шифрования двух случайных ключей на шаге 2.
  4. При необходимости сгенерируйте верификатор пароля, хешируя свой пароль достаточно большим количеством раз.

Конечный файл должен иметь следующую структуру, форматирование опущено [соль] [пароль испытатель] [зашифрованный ключ шифрования] [зашифрованный ключ HMAC] [зашифрован список паролей] [значение HMAC]

0

Вы можете заинтересуйтесь нашим новым SDK для SmartUtils Password: http://sutils.com/index.php/smartutils-password-sdk Он позволяет хранить пароли с соответствующей информацией, например URL-адресами, именами пользователей и т. д. в файле зашифрованной AES-256. Главный пароль может быть зашифрован с использованием DPAPI в одной строке кода.