辞書: SQLインジェクション

投稿日: セキュリティ SQL

SQLインジェクションとは

ユーザの入力値をSQLとして解釈してしまうプログラムによる脆弱性です。

SQLインジェクションを避ける原則

以下に書かれています。

実際は以下の2つでだいたい問題ありません。

Javaでの例

基本

// 悪い例
String sql = "SELECT * FROM USER WHERE USER_ID = '" + userId + "'";
Statement stmt = conn.createStatement(sql);
// 良い例
String sql = "SELECT * FROM USER WHERE USER_ID = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, userId);

入力値によってWHEREが変わるもの

// 悪い例
// userIdはフォームで渡されたパラメータとする。
String sql = "SELECT * FROM USER ";
if (userId != null) {
    sql += "WHERE USER_ID = '" + userId + "'";
}
Statement stmt = conn.createStatement(sql);
// 良い例
// SQLインジェクションではないですが、例が良くないのであとで書き換えます。。。
List<String> values = new ArrayList<>();
values.add(userId);
String sql = "SELECT * FROM USER ";
if (userId != null) {
    sql += "WHERE USER_ID = ?";
}

PreparedStatement stmt = conn.prepareStatement(sql);
int i = 1;
for(String value: values) {
    stmt.setString(i++, value);
}

IN句を作成する場合

// 悪い例
// userIdListはフォームで渡されたパラメータとする。
String sql = "SELECT * FROM USER WHERE USER_ID IN (";
for (String userId : userIdList) {
    sql += "'" + userId + "',";
}
sql = sql.substring(0, sql.length() - 1);
Statement stmt = conn.createStatement(sql);
// 良い例
// SQLインジェクションではないですが、コードはもっと良いものにできますね。。。
List<String> values = new ArrayList<>();
String sql = "SELECT * FROM USER WHERE USER_ID IN (";
for (String userId : userIdList) {
    sql += "?,";
    values.add(userId);
}
sql = sql.substring(0, sql.length() - 1);

PreparedStatement stmt = conn.prepareStatement(sql);
int i = 1;
for(String value: values) {
    stmt.setString(i++, value);
}

プリペアードステートメントでは対応できないもの

ソート順序

ソート順序については2種類しかないため、条件分岐で対応可能です。

// 悪い例
String orderBy = ...; // 画面から渡され、通常はASC/DESCのどちらかが入る。
String sql = "SELECT * FROM USER ORDER BY" + orderBy;
// 良い例
String orderBy = ...; // 画面から渡され、通常はASC/DESCのどちらかが入る。
String sql = "SELECT * FROM USER ";
switch(orderBy) {
    case "ASC":
        sql += "ORDER BY ASC";
        break;
    case "DESC":
        sql += "ORDER BY DESC";
        break;
    default:
        // エラー処理
}

列名、テーブル名

ある一定の列名、テーブル名のみ使用する場合は、 その中に含まれているかどうかのチェックで対応可能です。

// 良い例
List<String> columns = ...; // 画面から渡され、列名が入る。
List<String> validColumns = Arrays.asList("NAME", "USER_ID", ...);
String sql = "SELECT ";
for(String column: columns) {
    if (validColumns.contains(column) {
        sql += column + ",";
    }
}
sql = sql.substring(0, sql.length() - 1);
sql += " FROM USER";

もし、ある一定の列名、テーブル名というのが決められないのなら、 それはデータベース管理ソフトを除いて、設計に問題があります

セカンドオーダーSQLインジェクション

そんなものはなかった。

正しい実装をしていれば起きません。



ページトップへ戻る