3

ここで本当に明白な何かが欠けているかもしれないと思いますが、私はこれにあまりにも長い間苦労しており、私のC++はかなり錆びています(10年以上)

以下のコードは問題なく動作しますが、変数を lname のクエリに渡す必要があります。文字列または文字配列でクエリを作成すると、SQLWCHAR* のパラメーター タイプと互換性がないというエラーが表示されます。

以下のコードが sql インジェクションに対して脆弱であることはわかっていますが、これは分離されたシステムでの 1 回限りのヒットであるため、何よりもシンプルさを求めています...

SQLHENV env;
SQLHDBC dbc;
SQLHSTMT  sql_hStmt;
SQLRETURN ret;
SQLWCHAR outstr[1024];
SQLSMALLINT outstrlen;

SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);

ret = SQLDriverConnect(dbc, NULL, L"DSN=myDSN", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);

ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &sql_hStmt);
SQLWCHAR* SQL = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, SQL, SQL_NTS);

SQLFetch(sql_hStmt);
4

4 に答える 4

1

パラメータ化されたクエリ文字列を準備することをお勧めします。こちらをご覧ください

文字列を単純に連結して毎回新しいクエリを作成すると、サイトがSQLインジェクション攻撃を受けやすくなる可能性があります。

于 2013-03-11T02:19:27.490 に答える
1

ここには 2 つの問題があります。1 つは必要なクエリを含む文字列を作成することであり、もう 1 つはその文字列を引数として関数に渡すことです。

これらの C の境界に到達するまでは、できるだけ "C++" のままにすることをお勧めします。したがってstd::wstring、C スタイルの文字列にする必要があるポイントまで、文字列処理に使用する必要があります。

std::wstring statementText = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, const_cast<SQLWCHAR*>(statementText.c_str()), SQL_NTS);

c_str()メンバー関数はヌル終了配列 (つまり、C スタイルの文字列) へのポインターを返しますが、このポインターの型は;const wchar_t*です。つまり、この C スタイルの文字列の内容は変更できません。

これは問題SQLWCHAR*ですwchar_t*。データをそのままにしておくことを約束するものではありません。そのため、値からconst_castを削除するために ,を含めました。constc_str()

これは、一般的にやりたいことではありません。 const_castconst オブジェクトを変更するのは UB であるため、未定義の動作への扉を直接開くため、間違いなく最も恐ろしいキャストです。

const int x = 0;
const int* p = &x; // anyone using this pointer can't modify x

int* bad = const_cast<int*>(p); // but this one is not so good
*bad = 5; // undefined behavior

ただし、ここで問題ない理由は、渡された文字列を実際にSQLExecDirect変更しないためです。const が使用されていないのは単に実装エラーであるため、削除しても問題ありません。(この const の欠如の間違いは、C では非常に一般的です。)

変更可能なバッファが本当に必要な場合は、現在のバージョンの C++ (C++11) から始めて、これを安全に行うことができます。

std::wstring statementText = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, &statementText[0], SQL_NTS);

最初の要素のアドレスを取得しています。これは、それ自体が null で終了する配列にあります。別の C スタイルの文字列。ただし、今回は変更可能な配列があります。タイプはすでに一致しています。

(これが C++11 で問題ないことに注意する理由は、以前のバージョンである C++03 では技術的にこの動作が保証されていなかったためです。そうではありません.実用的には、どちらの方法でも問題ありません.)

どちらを使用するかはあなた次第です。常に使用するだけだと主張する人もいる&str[0]ので、UBは絶対にありません。関数が文字列を変更してconstをキャストするのではなく、最終的にconstの考え方で動作するという意図と信念を文書化することを主張します。何か悪いことが起こった場合、 const を付けておけばよかったと思うよりも、 const から離れてリラックスするのは簡単です。

注意すべき重要な点の 1 つは、これらの返されたすべてのポインター ( または のいずれstr.c_str()&str[0]) は、strオブジェクト自体が生きていて変更されていない場合にのみ有効であるということです。これは悪いです:

const wchar_t* get_query()
{
    std::wstring result = /* build query */;

    // oops, this pointer stops being meaningful when result stops existing!
    return result.c_str();
}

邪魔にならないので、これらの文字列を構築するのは簡単です。std::wstringstream: _

std::wstringstream ss; 

ss << "this is basically an expanding buffer that accepts anything std::wcout will";
ss << std::endl;
ss << "this includes integers " << 5 << " and other stream-insertable types";

したがって、おそらく次のようなものが必要です。

std::wstring build_query(const std::wstring& name)
{
    // you can provide a starting string
    std::wstringstream result(L"select * from DB.employees where lname=");

    result << "\'" << name << "\'";

    return result.str(); // this captures the buffer as a C++ string
}

// Remember, this would be bad!
//
// SQLWCHAR* SQL = const_cast<SQLWCHAR*>(build_query(L"Smith").c_str());
//
// Because the C++ string returned by build_query is temporary;
// it stops existing at the end of this full expression,
// so SQL would be a bad pointer. This is right:

std::wstring SQL = build_query(L"Smith");
ret = SQLExecDirect(sql_hStmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS);

それが役立つことを願っています。


また、マクロを除いてすべて大文字の識別子を使用することは避けたいと思います。なぜなら、C++ コードを読む人は、そのような名前が圧倒的にマクロであると予想しているからです。さらに、サンプル コードでは C++ スタイルのキャストを使用しました。あなたは同じことをすべきです。C スタイルのキャスト ( (type)value) は、強力すぎて安全ではありません。

于 2013-03-11T00:20:44.803 に答える
0

次の方法で簡単に実行できます。

あなたはそれを行うことができます

   int var = 10;
   string str = to_string(var);
   string requete="INSERT INTO stat(temps) VALUES (\"";
   requete += str;
   requete += "\")";
   mysql_query(&mysql,requete.c_str());

フィールドのタイプが int 、 double 、 float などであることを mySql で指定するだけです。

于 2015-08-18T15:44:50.770 に答える