コードを記述する前に、整理された String 型の Hql または SQL ステートメントを実行のためにバックグラウンドに渡します。
これは実際には非常に愚かなアプローチです。 ! ! !
ユーザーのログイン シナリオを真似してみましょう:
一般的なアプローチは、フロント デスクによって文字列として取得されたユーザー名とパスワードをクエリ ステートメントに動的に結合し、データベースを呼び出すことです。 query ~クエリの結果が null でない場合は、ユーザーが存在し、ログインが成功したことを意味します。それ以外の場合、ログインは失敗します。
通常、ユーザーはアカウント番号 123456 とパスワード 123 を入力します (パスワードが間違っているか、ユーザーがまったく存在しないと仮定します)
usernameString//前台输入的用户名passwordString//前台输入的密码//hql语句String queryString = "from User t where t.username= " + usernameString + " and t.password="+ passwordString;//执行查询List result = session.createQuery(queryString).list();
ユーザーが正常に入力すると、SQLステートメントは次のように結合されます: from User t where t.username=123456 and t.password=123 ;
これは通常の SQL ステートメントです。データベースにクエリを実行して、このユーザー データが存在するかどうかを確認できます。
でも!
ユーザーがパスワード入力ボックスに 123 または 1=1 を入力すると、文字列としてバックグラウンドに渡されます
SQL ステートメントは次のように結合されます: from User t where t.username= 123456 と t.password =123 または 1=1;
or 1=1 を追加すると、この SQL は常に確立されます。 ! !さらに深刻なケースには、データベース内のテーブルの削除や情報の改ざんが含まれます。これは非常に深刻です。 ! !
SQL インジェクションの理由は、表面的には、文字列が結合されて SQL ステートメントが形成され、SQL ステートメントがプリコンパイルされないか、バインドされた変数が使用されるためです。
しかし、より深い理由は、ユーザーが入力した文字列が「SQL文」として実行されることです。
たとえば、上記の String queryString = "from User t where t.username= " + usernameString + " and t.password="+passwordString;
ユーザー名とパスワードの値が必要ですユーザーが入力した文字列リテラルとして、実行のためにデータベースに渡されます。
しかし、次のように入力すると、 123 または 1=1 または 1=1 は、where id= のリテラル値として使用されず、SQL ステートメントとして実行されます。したがって、その本質は、ユーザーの入力データをコマンドとして実行することです。
基本的には、SQL ステートメントを使用して変数をプリコンパイルし、バインドすることが SQL インジェクションから防御する最良の方法であることを誰もが知っています。 SQL インジェクションを防ぐには、SQL ステートメントをつなぎ合わせないようにします。 ! !
実際のプロジェクトでは、ibatis、hibernate、mybatisなど、さまざまなフレームワークを使用することが一般的です。通常、デフォルトでは SQL がプリコンパイルされます。 ibatis/mybatis の場合、#{name} という形式を使用すると、SQL はプリコンパイルされます。${name} を使用すると、SQL はプリコンパイルされません。
hibernate は、JDBC スタイルの位置パラメータ (クエリ文字列で使用されますか?) をサポートします。これは、名前付きパラメータ (クエリ文字列: で使用されます) を使用するのと同じ効果があります。
名前付きパラメータを使用する
usernameString//前台输入的用户名passwordString//前台输入的密码//hql语句String queryString = "from User t where t.username:usernameString and t.password: passwordString";//执行查询List result = session.createQuery(queryString) .setString("usernameString ", usernameString ) .setString("passwordString", passwordString) .list();
位置パラメータを使用する
usernameString//前台输入的用户名passwordString//前台输入的密码//hql语句String queryString = "from User t where t.username=? and t.password=?";//执行查询List result = session.createQuery(queryString) .setString(0, usernameString ) .setString(1, passwordString) .list();
两者比较:positional parameter可读性强不如named parameter的强,而且可维护性差,如果我们的查询稍微改变一点,将第一个参数和第二个参数改变一下位置,
这样我们的代码中涉及到位置的地方都要修改,所以我们强烈建议使用named parameter方式进行参数绑定。
在举个栗子~~
我们模仿一下用户登录的场景:这次业务变换,有的网站,手机号可以作为用户名来登录,也能作为手机号本身登录。
常见的做法是将前台获取到的用户名or手机号和密码,作为字符串动态拼接到查询语句中,然后去调用数据库查询~查询的结果不为null就代表用户存在,则登陆成功,否则登录失败!
正常情况下用户输入账号是13812345678和密码123
这里usernameString作为手机号又作为用户名出现了两次,怎么办呢?
大家请看下面代码:
usernameString//前台输入的用户名passwordString//前台输入的密码//hql语句String queryString = "from User t where t.username:usernameString and t.phone:usernameString and t.password: passwordString";//执行查询List result = session.createQuery(queryString) .setString("usernameString ", usernameString ) .setString("passwordString", passwordString) .list();
在Hibernate+spring中getHibernateTemplate()返回的对象可以调用find(String queryString, Object value...Object value)来实现named parameter。比如:
usernameString//前台输入的用户名passwordString//前台输入的密码//hql语句String queryString = "from User t where t.username:usernameString and t.password: passwordString";//执行查询return getHibernateTemplate().find(queryString, usernameString, passwordString);
以上がHibernate を使用して SQL インジェクションを防ぐ方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。