injectionは「注入」の意味です。つまり不正なSQLコマンドを注入する攻撃です。
データベースを使うWebアプリケーションなどで、想定しないSQL文を実行させることで、
データベースシステムを不正に操作するという事件が起きています。
それは、攻撃を想定した対処を怠った脆弱性のあるシステムの場合に起こります。
左のフォームは、次のように作られています。
<form action="e71056a.php" method="post" target="blank"> <h1>ユーザーIDとパスワードを入力ください。</h1> <p> ユーザーID<input type="text" name="memberID" value="s102031" size="20"><br> パスワード<input type="password" name="passwd" value="abcxyz" size="20"><br> <input type="submit" value="ログイン"></p> </form> |
このページで使うデータベースを準備する手順を示します。
STEP 1
root権限で、goodsデータベースを次のコマンドで作成
mysql> CREATE DATABASE goods;
root権限で、ローカルアクセス「usrgd」のユーザーを「g7d5」のパスワードで作成
mysql> GRANT ALL PRIVILEGES ON goods.* TO 'usrgd'@'127.0.0.1' IDENTIFIED BY 'g7d5';
STEP 2
上記で作成した「usrgd」のユーザーでログインし直します
mysql --user=usrgd --password=g7d5 goods
mysql>
次の「memberTbl」テーブルを作成します。
create table memberTbl(
memberID char(8) not null,
lastName char(10),
firstName char(10),
city char(10),
address char(20),
tel char(15),
mail char(20),
passwd char(10),
primary key (memberID)
);
次の構成の中で、p_authorize.jspの処理で、 SQLインジェクションができます。
| ファイル | 動作概要 | ソース |
|---|---|---|
| MySQLのデータベースgoods | 各種データの記憶 | テーブル作成などの作成情報 |
| p_display.jsp | 全ユーザーリストの表示 | ソース |
| p_addition.htm | 新規データ入力(ボタンで下記のp_inert.jspを実行) | ソース |
| p_inert.jsp | 上記の新規データで、データーベースへ追加 | p_inert.jspのソース |
| p_login.html | ユーザーログインキー入力 | ソース |
| p_authorize.jsp | 上記ログインデータで認証判定 | p_authorize.jspのソース |
<HTML><BODY>
<%@ page
contentType="text/html; charset=Shift_JIS"
import="java.sql.*"
%>
<%
try {
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
// mysql-connector-java-5.1.7-bin.jar コネクタで、mysql5.0.67使用
//「?useUnicode=true&characterEncoding=SJIS」で日本語有効の接続指定
String connectStr = "jdbc:mysql://127.0.0.1:3306/goods?useUnicode=true&characterEncoding=SJIS";
Connection dbConn = DriverManager.getConnection(connectStr,"usrgd","g7d5");
Statement st = dbConn.createStatement();//静的 SQL 文を実行用オブジェクト取得
ResultSet rs = st.executeQuery("SELECT * FROM memberTbl");
ResultSetMetaData meta = rs.getMetaData(); //メタデータ取得
int columnNumber = meta.getColumnCount(); //列数を取得
String column[] = new String[columnNumber]; //列名記憶用
out.println("<table border><caption>memberTbl 表の内容</caption>\n");//表作成
out.println("<tr>");
for(int i = 0; i < columnNumber; i++){ //列名
column[i] = meta.getColumnName(i+1);
out.println("<td>" + column[i] + "</td>");
}
out.println("</tr>\n");
while (rs.next()) { //結果のレコード集合を列ごとに処理する繰り返し
out.println("<tr>");
for(int i = 0; i < column.length; i++){
byte []a = rs.getBytes(column[i]);
String data = "";
// for( int k = 0; k < a.length; k++)data += String.format("%2x ", a[k]);
data = new String(a, "Shift_JIS");//Shift_JIS,UTF-8,UTF-16,UTF-16BE,UTF-16LE,ISO-8859-1
out.println("<td>" + data + "</td>");
}
out.println("</tr>\n");
}
out.println("</table>\n");
st.close();
dbConn.close();
out.println("<br><br><br><a href='index.html'>index.htmlに戻ります。</a>\n");
}
catch ( Exception e) {
e.printStackTrace();
}
%>
</BODY></HTML>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>新規ユーザー追加</title>
<style type="text/css">
span.WB { font-family: monospace; background-color: black; color: white; }
pre.BW { border-style: solid; border-width: 1px;}
body { font-size: 14pt; margin-left: 10%; font-family: monospace;}
</style>
</head>
<body>
<h1 align="center">新規ユーザー追加</h1>
<form action="p_inert.jsp"
method="post" accept-charset="Shift_JIS">
各内容を適当に変更して送信ボタンをクリックください。<br>
(既に使われているIDの場合は登録できません)<br>
<br>
<table>
<tr>
<td>ID</td>
<td><input type="text" name="memberID" value="s987654" size="20"></td>
</tr><tr>
<td>姓</td>
<td><input type="text" name="lastName" value="yamada" size="20"></td>
</tr><tr>
<td>名</td>
<td><input type="text" name="firstName" value="tarou" size="20"></td>
</tr><tr>
<td>市、区</td>
<td><input type="text" name="city" value="tokyo" size="20"></td>
</tr><tr>
<td>住所</td>
<td><input type="text" name="address" value="sinjuku" size="40"></td>
</tr><tr>
<td>TEL</td>
<td><input type="text" name="tel" value="110" size="30"></td>
</tr><tr>
<td>mail</td>
<td><input type="text" name="mail" value="ta@hotmail.com" size="30"></td>
</tr><tr>
<td>password</td>
<td><input type="password" name="passwd" value="abcxyz" size="20"></td>
</tr>
</table>
<input type="submit" value="送信">
</form>
<p align="right">
<a href='index.html'>index.htmlに戻ります</a>
</p>
</body>
</html>
<HTML><BODY>
<%@ page
contentType="text/html; charset=Shift_JIS"
import="java.sql.*"
%>
<%
try{
//入力データの取得
String memberID = request.getParameter("memberID");//顧客ID
String lastName = request.getParameter("lastName");//姓
String firstName = request.getParameter("firstName");//名
String city = request.getParameter("city");//市、区
String address = request.getParameter("address");//住所
String tel = request.getParameter("tel");//TEL
String mail = request.getParameter("mail");//mail
String passwd = request.getParameter("passwd");//passwd
lastName = new String(lastName.getBytes("iso-8859-1"),"Shift_JIS");//JISAutoDetect
firstName = new String(firstName.getBytes("iso-8859-1"),"Shift_JIS");
city = new String(city.getBytes("iso-8859-1"),"Shift_JIS");
address = new String(address.getBytes("iso-8859-1"),"Shift_JIS");
out.println(memberID + "<br>");
out.println(lastName + "<br>");
out.println(firstName + "<br>");
out.println(city + "<br>");
out.println(address + "<br>");
out.println(tel + "<br>");
out.println(mail + "<br>");
out.println(passwd + "<br>");
//SQL作成
String sql="INSERT INTO memberTbl(memberID,lastName,firstName,city,address,tel,mail,passwd) VALUES (";
sql+="'" + memberID + "',";
sql+="'" + lastName + "',";
sql+="'" + firstName + "',";
sql+="'" + city + "',";
sql+="'" + address + "',";
sql+="'" + tel + "',";
sql+="'" + mail + "',";
sql+="'" + passwd + "');";
//sql = "INSERT INTO memberTbl(memberID,lastName,firstName,city,address,tel,mail,passwd) VALUES ('s9876','山田','太郎','東京','文京区','110','ta@hotmail.com','abcxyz');";
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
// mysql-connector-java-5.1.7-bin.jar コネクタで、mysql5.0.67使用
//「?useUnicode=true&characterEncoding=SJIS」で日本語有効の接続指定
String connectStr = "jdbc:mysql://127.0.0.1:3306/goods";
Connection dbConn = DriverManager.getConnection(connectStr,"usrgd","g7d5");
Statement st = dbConn.createStatement();//静的 SQL 文を実行用オブジェクト取得
out.println("<pre>次のSQL文を使いました。\n");
out.println( sql );
out.println("</pre>");
int r;
//SQL 文を実行し、結果を得る
r = st.executeUpdate( sql );
if(r == 1){
out.println( "上記データが追加されました。" );
} else {
out.println( "上記データは追加できませんでした。「" + memberID + "」のIDが既に使われています。" );
}
out.println("<br><br><br><a href='index.html'>index.htmlに戻ります。</a>\n");
st.close();
dbConn.close();
}
catch ( Exception e) {
out.println( e.toString() + "<br>" );
out.println( e.getMessage() );
e.printStackTrace();
}
%>
</BODY></HTML>
<!DOCUMENT HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML Lang="ja">
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<TITLE>login Page</TITLE>
<style type="text/css">
body { font-size: 14pt; margin-left: 10%; font-family: monospace;}
</style>
</HEAD>
<BODY bgcolor="lightblue" text="black" Link="navy" vLink="navy">
<FORM ACTION="p_authorize.jsp" METHOD="POST">
<h1>ユーザーIDとパスワードを入力ください。</h1>
<p>
ユーザーID<INPUT TYPE="TEXT" NAME="memberID" value="" size="20"><BR>
<BR>
パスワード<INPUT TYPE="password" NAME="passwd" value="" size="20"><BR>
<BR>
<INPUT TYPE="SUBMIT" NAME="転送" VALUE="ログイン">
</p>
<p align="right">
<a href='index.html'>index.htmlに戻ります</a>
</p>
</FORM>
</BODY>
</HTML>
<HTML><BODY>
<%@ page
contentType="text/html; charset=Shift_JIS"
import="java.sql.*"
%>
<%
try{
//入力データの取得
String memberID = request.getParameter("memberID");//顧客ID
String passwd = request.getParameter("passwd");//passwd
//SQL作成
String sql="SELECT * FROM memberTbl WHERE ";
sql+="memberID ='" + memberID;
sql+="' AND passwd = '" + passwd + "';";
out.println("<pre>次のSQL文を使いました。\n");
out.println( sql );
out.println("</pre>");
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
// mysql-connector-java-5.1.7-bin.jar コネクタで、mysql5.0.67使用
//「?useUnicode=true&characterEncoding=SJIS」で日本語有効の接続指定
String connectStr = "jdbc:mysql://127.0.0.1:3306/goods";
Connection dbConn = DriverManager.getConnection(connectStr,"usrgd","g7d5");
Statement st = dbConn.createStatement();//静的 SQL 文を実行用オブジェクト取得
ResultSet rs = st.executeQuery( sql );
if(rs.next()){
ResultSetMetaData meta = rs.getMetaData(); //メタデータ取得
int columnNumber = meta.getColumnCount(); //列数を取得
String column[] = new String[columnNumber]; //列名記憶用
out.println("<table border><caption>認証できました。</caption>\n");//表作成
out.println("<tr>");
for(int i = 0; i < columnNumber; i++){ //列名
column[i] = meta.getColumnName(i+1);
out.println("<td>" + column[i] + "</td>");
}
out.println("</tr>\n");
do {//結果のレコード集合を列ごとに処理する繰り返し
out.println("<tr>");
for(int i = 0; i < column.length; i++){
byte []a = rs.getBytes(column[i]);
String data = "";
// for( int k = 0; k < a.length; k++)data += String.format("%2x ", a[k]);
data = new String(a, "Shift_JIS");//Shift_JIS,UTF-8,UTF-16,UTF-16BE,UTF-16LE,ISO-8859-1
out.println("<td>" + data + "</td>");
}
out.println("</tr>\n");
}while (rs.next());
out.println("</table>\n");
} else {
out.println("認証できません!\n");
}
out.println("<br><br><br><a href='index.html'>index.htmlに戻ります。</a>\n");
st.close();
dbConn.close();
}
catch ( Exception e) {
out.println( e.toString() + "<br>" );
out.println( e.getMessage() );
e.printStackTrace();
}
%>
</BODY></HTML>
なお、不具合が生じるパスワードのキーワードの例は、『' OR 1='1』です。