Хранение MD5 в MySQL

#mysql #delphi #hash #md5

#mysql #дельфи #хэш #md5

Вопрос:

Вместо того, чтобы хранить a MD5 hash в 32-байтовом поле, я хотел бы сохранить его в 16-байтовом двоичном поле. Поле Mysql «TEMP_MD5» определяется как двоичное (16).

Таблица СОЗДАНИЯ MySQL с примером вставки строки:

 CREATE TABLE `mytable` (
    `TEMP_MD5` binary(16) DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO mytable (TEMP_MD5) VALUES UNHEX("202cb962ac59075b964b07152d234b70") );
 

Пример кода:

Допустим, после того, как 16-байтовое двоичное поле было сохранено в поле MySQL TEMP_MD5, как мне сравнить это 16-байтовое поле в коде Delphi после извлечения значения?

Можно ли пропустить внутренние функции MySQL HEX / UNHEX и просто использовать код Delphi для сравнения 16-байтового двоичного поля (32-байтовая строка) в MySQL?

Например :

 FDQuery1.Open( 'SELECT TEMP_MD5 from mytable;' );

if THashMD5.GetHashBytes('123') = fDQuery1.FieldByName('TEMP_MD5').VALUE then
  SHOWMESSAGE('MATCHED!');
 

Однако, похоже, что значения для FieldByName(‘TEMP_MD5’).value никогда не совпадали со значением THashMD5.GetHashString(‘123’)

и другой способ сравнения с помощью оператора SELECT также не удался

 FDQuery1.Open( 'SELECT TEMP_MD5 mytable '  
                        'WHERE (TEMP_MD5=:myvalue)',
                               [THashMD5.GetHashBytes('123')] );
 

выше также не удалось дать FDQuery1.RecordCount = 1 .

По сути, я пытаюсь сравнить 16-байтовый двоичный файл, который я сохранил в MySQL, со значением, скажем, «123» в коде, чтобы увидеть, совпадают ли оба.

Я использую Delphi 10.2, переходя на 10.4 в следующем году.

Комментарии:

1. Добавьте 2-3 примера хэш-значений, которые вы пытаетесь сохранить. Добавьте скрипт СОЗДАНИЯ ТАБЛИЦЫ для MyTable .

2. Я обновил свой вопрос, чтобы уточнить. добавлена простая инструкция Create Table. Спасибо.

3.Вы забыли поменять двоичный файл на строку. FDQuery1.Open( 'SELECT LOWER(HEX(TEMP_MD5)) TEMP_MD5 from mytable;' ); dbfiddle.uk /… PS. Вы потеряли одну круглую скобку в INSERT INTO — check в скрипке.

4. мои извинения. Я пропустил скобки при вставке. о, да, я должен вернуть шестнадцатеричное значение. Спасибо!

5. На самом деле, просто чтобы уточнить, я хочу свести к минимуму сервер, выполняющий шестнадцатеричное значение в поле ВЫБОРА, я пытаюсь сравнить 16-байтовый двоичный файл в таблице с 16-байтовым двоичным значением, заданным THashMD5.GetHashBytes(‘123’) … HEX снова преобразует его в 32-байтовую строку (я полагаю?). чего я хочу избежать. Разве я не могу просто сравнить, совпадают ли 2 16-байтовых двоичных значения?

Ответ №1:

Вот пример кода, показывающий, как записать MD5 в вашу базу данных и как прочитать его обратно и сравнить с заданным хэшем MD5:

Вставка данных:

 procedure TForm1.InsertDataButtonClick(Sender: TObject);
var
    MD5    : TArray<Byte>;
begin
    MD5 := THashMD5.GetHashBytes('123');
    FDConnection1.Connected := TRUE;
    FDQuery1.SQL.Text := 'INSERT INTO mytable (TEMP_MD5) VALUES(:MD5)';
    FDQuery1.ParamByName('MD5').SetBlobRawData(Length(MD5), PByte(MD5));
    FDQuery1.ExecSQL;
    Memo1.Lines.Add('Rows affected = '   FDQuery1.RowsAffected.ToString);
end;
 

Считывание данных обратно и сравнение с заданным хэшем:

 procedure TForm1.ReadDataButtonClick(Sender: TObject);
var
    MD5      : TArray<Byte>;
    MD5_123  : TArray<Byte>;
    FieldMD5 : TField;
    RecCnt   : Integer;
begin
    MD5_123 := THashMD5.GetHashBytes('123');

    FDConnection1.Connected := TRUE;
    // First version: get all records
    // FDQuery1.SQL.Text := 'SELECT TEMP_MD5 FROM mytable';
    // Second version: Get only records where TEMP_MD5 is hash('123').
    FDQuery1.SQL.Text := 'SELECT TEMP_MD5 FROM mytable WHERE TEMP_MD5 = :MD5';
    FDQuery1.ParamByName('MD5').SetBlobRawData(Length(MD5_123), PByte(MD5_123));
    // Execute the query
    FDQuery1.Open;
    RecCnt := 0;
    while not FDQuery1.Eof do begin
        Inc(RecCnt);
        FieldMD5 := FDQuery1.FieldByName('TEMP_MD5');
        SetLength(MD5, FieldMD5.DataSize);
        FieldMD5.GetData(MD5);
        if (Length(MD5) = Length(MD5_123)) and
           (CompareMem(PByte(MD5), PByte(MD5_123), Length(MD5))) then
            Memo1.Lines.Add(RecCnt.ToString   ') MD5(123) = '   MD5ToStr(MD5))
        else
            Memo1.Lines.Add(RecCnt.ToString   ') '   MD5ToStr(MD5));
        FDQuery1.Next;
    end;
end;
 

Как вы можете видеть, читая код, я сравниваю MD5 из базы данных с заданным MD5, сравнивая память, содержащую значения (массивы байтов).

Служебная функция:

 function MD5ToStr(MD5 : TArray<Byte>) : String;
var
    B      : Byte;
begin
    Result := '';
    for B in MD5 do
        Result := Result   B.ToHexString(2);
end;
 

Комментарии:

1. спасибо, @fpiette. просто любопытно … нет более простого способа прочитать двоичное (16) значение из таблицы и преобразовать его обратно в MD5 во время выполнения? Что-то вроде FDQuery1. FieldByName(‘TEMP_MD5’). AsBytes или что-то в этом роде?

2. Если я смогу заставить это работать, FDQuery1.Open( ‘SELECT * from mytable WHERE (TEMP_MD5=:myvalue)’,[THashMD5.GetHashBytes(‘123’)] ), где и поле таблицы, и мой параметр являются 16-байтовыми двоичными значениями, это также будет работать для меня (сократитекод). Как заставить выше работать?

3. @PeterJones тот самый «. AsBytes» ваш запрос называется «.getData». И это метод, использующий буфер для хранения данных. Для меня это выглядит так просто: FDQuery1. FieldByName(‘TEMP_MD5’). getData(MD5); Поскольку я использовал TBytes , то есть динамически выделяемый тип данных, ему должен предшествовать необходимый код для выделения достаточного пространства. Требуемое пространство задается с помощью DataSize. Поскольку поле используется дважды и является медленным методом, я сохранил его значение в переменной.

4. @PeterJones Я отредактировал свой ответ, добавив вторую версию для параметризованного запроса с предложением where. Попробуй это сделать. Если это сработает для вас, пожалуйста, отметьте мой ответ как принятый (галочка на левой стороне моего ответа) и, возможно, также проголосуйте за него (стрелка вверх слева от моего ответа).

5. большое спасибо! Сейчас у меня есть только 1 очко репутации, а для повышения рейтинга требуется 15 очков репутации? кто-нибудь, пожалуйста, проголосует за ответ сейчас? Я обещаю вернуться к голосованию за ответ, как только наберу 15 баллов!