Добавление записей в файл MS Access с помощью oledb для


В следующий фрагмент кода я добавляю записи в файл MS Access с помощью oledb для. Цель этого поста-обратить внимание на мой плохой практикой программирования, и если я создал здесь какие-либо изъяны в безопасности. Я немного читал про SQL-инъекции, и я интересно, если этот код может иметь любые потенциальные ошибки.

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

private void btnAddEntry_Click(object sender, EventArgs e)
{
    // Multiple level field validations.
    if (cmbType.SelectedIndex != -1)
    {
        if (cmbType.SelectedIndex == 0 &&
            (!string.IsNullOrEmpty(txtUserName.Text.Trim()) &&
            !string.IsNullOrEmpty(txtPassword.Text.Trim())))
        {
            string SQL =
                "INSERT INTO PersonalData([Type], [UserName], [Password]) " +
                "VALUES(@Type, @UserName, @Password)";

            InsertData(SQL);
        }
        else if (cmbType.SelectedIndex == 1 &&
            (!string.IsNullOrEmpty(txtURL.Text.Trim()) &&
            !string.IsNullOrEmpty(txtUserName.Text.Trim()) &&
            !string.IsNullOrEmpty(txtPassword.Text.Trim())))
        {
            // Creating SQL string. Using [] will prevent any erros
            // that might occur if any other names will be reserved words.
            string SQL =
                "INSERT INTO PersonalData([Type], [URL], [UserName], [Password]) " +
                "VALUES(@Type, @URL, @UserName, @Password)";

            InsertData(SQL);
        }
        else if (cmbType.SelectedIndex == 2 &&
            (!string.IsNullOrEmpty(txtSoftwareName.Text.Trim()) &&
            !string.IsNullOrEmpty(txtSerialCode.Text.Trim())))
        {
            // Creating SQL string. Using [] will prevent any erros
            // that might occur if any other names will be reserved words.
            string SQL =
                "INSERT INTO PersonalData([Type], [SoftwareName], [SerialCode]) " +
                "VALUES(@Type, @SoftwareName, @SerialCode)";

            InsertData(SQL);
        }
        else
        {
            lblMessage.Text = "Please fill out all required fields!";
        }
    }
    else
    {
        lblMessage.Text = "Please select a type first!";
    }
}

Во-вторых, это фактический код, который вставляет данные в файл:

private void InsertData(string sql)
{
    // Initialising encrypting/decrypting file.
    Security security = new Security();

    using (OleDbConnection connection = new OleDbConnection())
    {
        // Creating command object.
        connection.ConnectionString =
            "Provider=Microsoft.ACE.OLEDB.12.0;" +
            "Data Source=" + filePath + ";" +
            "Persist Security Info=False;" +
            "Jet OLEDB:Database Password=" + hashPhrase.ShortHash(pass) + ";";

        using (OleDbCommand command = new OleDbCommand(sql, connection))
        {
            // I need to make sure that I am using correct parameters
            // when using specified combo type. If I wouldn't
            // differenciate between types, then I would end up
            // with empty fields in the database file.
            if (cmbType.SelectedIndex == 0)
            {
                OleDbParameter prmType = new OleDbParameter
                    ("@Type", security.EncryptAES(cmbType.Text, pass, user));
                OleDbParameter prmUserName = new OleDbParameter
                    ("@UserName", security.EncryptAES(txtUserName.Text, pass, user));
                OleDbParameter prmPassword = new OleDbParameter
                    ("@Password", security.EncryptAES(txtPassword.Text, pass, user));

                command.Parameters.Add(prmType);
                command.Parameters.Add(prmUserName);
                command.Parameters.Add(prmPassword);
            }
            else if (cmbType.SelectedIndex == 1)
            {
                OleDbParameter prmType = new OleDbParameter
                    ("@Type", security.EncryptAES(cmbType.Text, pass, user));
                OleDbParameter prmURL = new OleDbParameter
                    ("@URL", security.EncryptAES(txtURL.Text, pass, user));
                OleDbParameter prmUserName = new OleDbParameter
                    ("@UserName", security.EncryptAES(txtUserName.Text, pass, user));
                OleDbParameter prmPassword = new OleDbParameter
                    ("@Password", security.EncryptAES(txtPassword.Text, pass, user));

                command.Parameters.Add(prmType);
                command.Parameters.Add(prmURL);
                command.Parameters.Add(prmUserName);
                command.Parameters.Add(prmPassword);
            }
            else if (cmbType.SelectedIndex == 2)
            {
                OleDbParameter prmType = new OleDbParameter
                    ("@Type", security.EncryptAES(cmbType.Text, pass, user));
                OleDbParameter prmSoftwareName = new OleDbParameter
                    ("@SoftwareName", security.EncryptAES(txtSoftwareName.Text, pass, user));
                OleDbParameter prmSerialCode = new OleDbParameter
                    ("@SerialCode", security.EncryptAES(txtSerialCode.Text, pass, user));

                command.Parameters.Add(prmType);
                command.Parameters.Add(prmSoftwareName);
                command.Parameters.Add(prmSerialCode);
            }

            try
            {
                connection.Open();
                command.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message);
            }
        }
    }

    // Refreshing state of main window.
    mainWindow.DisplayFileContent(filePath);

    lblMessage.Text = "Data was successfully added.";

    // Clearing all fields.
    ClearFields();
}


722
4
задан 3 декабря 2011 в 05:12 Источник Поделиться
Комментарии
4 ответа

Совершенно не имеющих отношения к шифрованию, я хотел бы обеспечить обратную связь на ваш btnAddEntry_Click и InsertData функции.

Самую большую проблему я вижу здесь в том, что ваш интеллект базе частично в код этой кнопки, и функция вставки имеет несколько проверок на UI. Что такое кнопка? Это контроль поставил в интерфейс, который пользователь может активировать для выполнения действий. Что запрос на вставку? Это команды, отправленные в базу данных, чтобы добавить информацию. Какая связь между двумя? Там действительно не один.

Кнопка не ваша программа. Форма не ваша программа. И ваша программа не ваша форма. Логику ты написал.

Во-первых, давайте упростим выбор. Что произойдет, если вы должны были добавить новый тип вставки? Вам нужно отредактировать две функции. Много работы, очень легко ошибиться. Вместо этого, вы можете привязать выбор к списку:

public enum EntryType
{
[StringValue("")]
None,
[StringValue("User")]
User,
[StringValue("User and URL")]
UserAndURL,
[StringValue("Software")]
Software
}

В StringValue атрибут берется от сюда:

public class StringValue : System.Attribute
{
public string Value { get; set; }

public StringValue(string value)
{
Value = value;
}
}

Плюс эта функция позволяет вам атрибута:

using System;
using System.Reflection;

public static class StringEnum
{
public static string GetStringValue(Enum value)
{
string output = String.Empty;
Type type = value.GetType();

FieldInfo fi = type.GetField(value.ToString());
StringValue[] attrs =
fi.GetCustomAttributes(typeof(StringValue),
false) as StringValue[];
if (attrs.Length > 0)
output = attrs[0].Value;

return output;
}
}

И этот метод расширения, чтобы привязать поле со списком для перечисления со строковыми значениями

    public static void BindToEnum<TEnum>(this ComboBox combo)
{
Dictionary<string, TEnum> enumList =
new Dictionary<string, TEnum>();

foreach (var enumValue in Enum.GetValues(typeof(TEnum)))
enumList.Add(StringEnum.GetStringValue((Enum)enumValue), (TEnum)enumValue);

// Bind the custom type combo box
combo.DisplayMember = "Key";
combo.ValueMember = "Value";
combo.DataSource = new BindingSource(enumList, null);
}

Наконец, в конструкторе формы:

public frmPersonalDataEntry()
{
InitializeComponent();
cmbType.BindToEnum<EntryType>();
}

Теперь у нас есть твердые перечисления для работы с и что отображает. Основная идея с btnAddEntry_Click функция может только быть большой переключатель:

switch ((EntryType)cmbType.SelectedValue)
{
case EntryType.None:
//do stuff
break;
case EntryType.User:
//do stuff
break;
case EntryType.UserAndURL:
//do stuff
break;
case EntryType.Software:
//do stuff
break;
}

Базы данных логика может быть в отдельном классе, который я назвал PersonalDataEntry. Я использую PersonalDataEntry объект с конструктором, как:

public PersonalDataEntry(string user, string pass, string filePath)

Это позволит обрабатывать ваши записи. Ваша форма должна иметь один объект этого класса, он не должен быть переделан на каждую запись.

В InsertData, не повторяйте свой выключатель. InsertData не должны быть известны форма. Я положил InsertData в PersonalDataEntry класс:

private bool InsertData(string insertQuery, string[] paramNames, object[] paramValues, out string message)
{
message = "";
// Initialising encrypting/decrypting file.
Security security = new Security();

using (OleDbConnection connection = new OleDbConnection())
{
// Creating command object.
connection.ConnectionString = String.Format(connectionString, filePath, hashPhrase.ShortHash(pass));
using (OleDbCommand command = new OleDbCommand(insertQuery, connection))
{
for (int i = 0; i < paramNames.Length; i++)
command.Parameters.Add(new OleDbParameter(paramNames[i],security.EncryptAES(paramValues[i].ToString(),pass,user)));

try
{
connection.Open();
command.ExecuteNonQuery();
return true;
}
catch (Exception ex)
{
message = "Error: " + ex.Message;
return false;
}
}
}
}

Хитрость заключается в том, чтобы просто собрать остальных. Каждый "делать вещи" надо заменить на функцию в объект PersonalDataEntry, который будет обрабатывать логику (текст запроса, переменные, проверять, если текст является допустимым, и т. д.). Я загрузил полную демо-проект.

4
ответ дан 4 декабря 2011 в 07:12 Источник Поделиться

Я не вижу никаких уязвимостей SQL, но я вижу несколько ошибок крипто.


  1. Вы используете тот же пароль для входа в базу данных и содержание. Вы не должны использовать пароли, потому что это увеличивает риск утечки.

  2. АЭС имеет два параметра: ключ и вектор инициализации (IV) для. Даже если вы используете тот же ключ, вы должны действительно быть через другую капельницу, чтобы избежать корреляции. (Напр. то же новое значение типа шифруется под один и тот же ключ с тем же IV будет такой же; если вы используете другой IV, вы можете избежать злоумышленник, будучи в состоянии сопоставить их). Было бы лучше, чтобы получить случайных IV (используя безопасную ГСЧ) и магазин, что в другом столбце.

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

5
ответ дан 3 декабря 2011 в 10:12 Источник Поделиться

Вы строите свои запросы в двух местах, что делает его трудно читать/понимать код. Мне нужно, чтобы прыгать туда и обратно между двумя мест, где вы можете оценить стоимость cmbType.Свойства selectedIndex , чтобы увидеть запрос, и какие бы параметры он получает. Вы повторяете, если/еще заявления, если через два различных метода.

Вместо этого вы должны попытаться придумать способ, чтобы установить параметры, которые идут с каждым запросом близко к тому, где вы создаете запрос. (Если это единственный способ, вы могли бы, возможно, создать команду sqlcommand объект(ы) в одном месте, и отправить его в InsertData способ вместо SQL-строку.

3
ответ дан 4 декабря 2011 в 03:12 Источник Поделиться

Я хотел найти код легче читать, если вы тестировали cmbType , чтобы увидеть, если вы можете выйти пораньше, вместо того, чтобы спасать его до конца. (с другого блока)

В этот момент ваш код немного, как это:

if (cmbType.SelectedIndex != -1)
{
// ... very many lines of code
}
else
{
lblMessage.Text = "Please select a type first!";
}

... и, заменив его на это:

if (cmbType.SelectedIndex == -1)
{
lblMessage.Text = "Please select a type first!";
return;
}

// ... very many lines of code

Было бы проще выполнять код (значение), поскольку ты не прыгаешь так много линий вверх и вниз в код, чтобы следить за потоком.

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

2
ответ дан 4 декабря 2011 в 03:12 Источник Поделиться