The best way has got to be parameterised queries. Then it doesn't matter what the user types in the data goes to the database as a value.
A quick search online shows some possibilities in PHP which is great! Even on this site - http://php.net/manual/en/pdo.prepared-statements.php
which also gives the reasons this is good both for security and performance.
SQLインジェクション
多くの開発者はSQLクエリがどのように改竄されるかということを余り 気にかけておらず、またSQLクエリは信用できるものと考えているようです。 実際にはSQLクエリはアクセス制限を回避することが可能で、従って 通常の認証や権限のチェックを無視することができます。時には、 OSレベルのコマンドを実行できてしまうこともあります。
ダイレクトSQLコマンドインジェクション(SQLコマンドの直接実行)という手法は、 攻撃者がSQLコマンドを生成もしくは既存のコマンドを変更することで 隠蔽すべきデータを公開したり、重要なデータを書き換えたり、データベース ホストで危険なシステムレベルのコマンドを実行したりするものの事です。 この手法は、ユーザーからの入力をスタティックなパラメータと組み合わせて SQLクエリを生成するアプリケーションにおいて使用されます。以下の例は 不幸なことに実際の事例に基づいたものです。
入力のチェックを怠っており、スーパーユーザーもしくはデータベース作成権限を 持つユーザー以外のユーザーでデータベースに接続していない ために、攻撃者はデータベースにスーパーユーザーを作成することが出来ます。
例1 表示するデータを分割し ... そしてスーパーユーザーを作成します。 (PostgreSQLの例)
$offset = $argv[0]; // 入力チェックが行われていません!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select 'crack', usesysid, 't','t','crack'
from pg_shadow where usename='postgres';
--
注意:
SQLパーサにクエリの残りの部分を無視させるために開発者によく使わ れる技法として、SQLのコメント記号である--があ ります。
パスワードを取得する恐るべき手段に、サイトの検索結果のページを欺く というものがあります。攻撃する者が必要とするものは、投稿された変数 の中でSQL命令で使用される際に正しく扱われていないものがあるかどう かを確かめるだけです。これらのフィルタは、通常、 SELECT文のWHERE, ORDER BY, LIMIT及びOFFSET句をカスタマイズするた めに前に置かれる形で設定されます。使用するデータベースが UNION構造をサポートしている場合、 攻撃者は元のクエリに任意のテーブルからパスワードのリストを取得する クエリを追加しようとするかもしれません。 暗号化されたパスワードフィールドを使用することが強く推奨されます。
例2 記事...そして(全てのデータベースサーバーの)いくつかのパスワード のリストを表示する
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'
ORDER BY $order LIMIT $limit, $offset;";
$result = odbc_exec($conn, $query);
' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; --
SQL UPDATE もデータベースを攻撃するために使用されます。これらのク エリも切捨てたり新しいクエリを元のクエリに追加することによる攻撃 を受けます。しかし、攻撃者はSET句を使用する可 能性があります。この場合、クエリを成功させるためにいくつかのスキー マ情報を保有する必要があります。これは、フォームの変数名や総当た り法により調べることができます。パスワードまたはユーザー名を保存す るフィールド用の命名記法はそう多くはありません。
例3 パスワードのリセットから ... (全てのデータベースサーバーで)より多 くの権限を得るまで
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
// $uid == ' or uid like'%admin%'; --
$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --";
// $pwd == "hehehe', admin='yes', trusted=100 "
$query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ...;"
恐ろしい例として、いくつかのデータベースホストのオペレーティン グシステムレベルのコマンドがアクセス可能となる方法を示します。
例4 データベースホストのオペレーティングシステムを攻撃する (MSSQLサーバー)
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
$query = "SELECT * FROM products
WHERE id LIKE '%a%'
exec master..xp_cmdshell 'net user test testpass /ADD'--";
$result = mssql_query($query);
注意:
上記のいくつかの例は、データベースサーバーの種類に依存しています。 これは、他の製品に対して同様な攻撃ができないことを意味するもので はありません。使用しているデータベースが他の手段で攻撃可能である 可能性もあります。
回避策
攻撃者がデータベースの構造に関して最低限の知識を持っていないと攻撃は成功しないということは明らかですが、 その手の情報はたいてい、簡単に入手できます。 たとえば、オープンソースやその他一般に公開されているソフトウェアパッケージをデフォルトの設定で使っていれば、 データベースの情報は完全に公開されているので誰でも知ることができます。 クローズドソースのコードであってもこの手の情報は漏れることがあります。 たとえ何らかの難読化処理が行われていたとしても。 さらに、自作のコードだとしても、 画面に表示されるエラーメッセージなどから情報が漏れることがあります。 それ以外にも、ありがちなテーブル名やカラム名などは攻撃の対象となります。 たとえば、ログインフォームで使っているテーブル名が 'users' で、その中に 'id'、'username'、'password' といったカラムがある場合などです。
これらの攻撃は、セキュリティを考慮して書かれていないコードを攻撃 する方法です。特にクライアント側から入力されるあらゆる種類の入力 を決して信用しないでください。これは、selectボックスやhidden input フィールド、Cookieの場合も同様です。最初の例は、このような欠点の ないクエリが破滅をもたらしうることを示すものです。
- データベースにスーパーユーザーまたはデータベースの所有者として 接続しないでください。 非常に制限された権限を有するカスタマイズ されたユーザーを常に使用してください。
- 指定された入力が期待するデータ型であることを確認してください。 PHPは、多くの種類の入力検証用関数を有しており、 変数関連の関数や 文字型関数にある簡単な関数 (例: それぞれ、is_numeric(), ctype_digit()) や、 Perl互換の正規表現のサポートま であります。
-
アプリケーションが、数値入力を期待している場合、データを is_numeric()で検証するか、 settype()により暗黙の型変換を行うか、 sprintf()により数値表現を使用することを検討 してみてください。
例5 ページング用のクエリを構築するためのより安全な方法
settype($order, 'integer');
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
// フォーマット文字列の%dに注意してください。%sを使用しても意味がありません。
$query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
$offset); - データベースに渡される数値以外のユーザー入力を データベース固有の文字列エスケープ関数 (mysql_real_escape_string(), sqlite_escape_string() など) でクオートしてください。 データベース固有の文字列エスケープ機能が利用できない場合、 addslashes() および str_replace()関数が利用できるでしょう。 (データベースの型に依存) 最初の例を参照 してください。前期の例が示すように、クエリの静的な部分をクオート するだけでは充分ではなく、簡単にクラックされてしまう可能性があ ります。
- データベース固有の情報、特にスキーマに関する情報は出力してはい きません。エラー出力およ びエラー処理およびログ関数 も参照ください。
- ユーザーがテーブルまたはビューに直接アクセスできないように、 データアクセスを抽象化することを目的としてストアドプロシージャ 及び事前に定義したカーソルを使用することもできますが、このソリュー ションには、副作用があります。
これらのケースにおいて、スクリプトまたはサポートされている場合は データベース自体でクエリのログをとることが有益です。 明らかにログは破壊的な行為を防止することはできませんが、攻撃され たアプリケーションを追跡する際には有効です。ログ自体は有益ではあ りませんが、含まれている情報は有益です。通常、より詳細なログをと る方が良いでしょう。
A good way to counter SQL injection for queries of type SELECT is use hash function on data by PHP and the database server.
For example, it is possible to use the MySQL function MD5 () to produce a hash of data-server side , and the equivalent function in php web-server side.
<?php
$login = mysql_query("select f_uname, f_passwd from t_user where MD5(f_uname) = '".md5($uname)."' and MD5(f_passwd)='".md5($passwd)."'");
?>
Thus, the injected requests will be crushed and it will become much more difficult to obtain data in the database. Use both sides of the hash result in a comparison of hash, not the execution of the injected queries.
Unfortunately, it probably does not work with other types of queries.
Another suggestion would be to build a series of DB procedures / functions that you give the DB user access to to manipulate or select data. That way, all input would run through this exposed interface and all parameters are forced to be typecast (or rejected).
Pangolin is an automatic SQL injection penetration testing tool developed by NOSEC.
Its goal is to detect and take advantage of SQL injection vulnerabilities on web applications. Once it detects one or more SQL injections on the target host, the user can choose among a variety of options to perform an extensive back-end database management system fingerprint, retrieve DBMS session user and database, enumerate users, password hashes, privileges, databases, dump entire or user"s specific DBMS tables/columns, run his own SQL statement, read specific files on the file system and more.
another way to stop sql injection when you odbc_*: create two users,
one has only select permission,
the other has only delete, update, and insert permission,
so you can use select-only user to call odbc_exec while you don't have to check the sql injection; and you use d/u/i only user to update database by calling odbc_prepare and odbc_execute.
Another way to prevent SQL injections as opposed to binary, is to use URL Encoding or Hex Encoding.
I haven't seen a complete example of stopping SQL Injections, most refer to use the mysql_real_escape_string function or param statements.
Several examples at http://en.wikipedia.org/wiki/SQL_injection
Which will stop \x00, \n, \r, \, ', " and \x1a based attacks.
Alot depends on your SQL query structure, though vector level attacks are still viable.
Other than that build your own regex replacement to protect specific queries that could alter or compromise your database/results for specific sections of your processing pages.
Also use unique table and field names. Not just putting _ infront of them...
Example, don't store User/s or Customer/s information in a table named the same.
And NEVER use the same form field names for database field names.
i just played around with the array_walk function.
It suddenly struck me that almost all super globals are arrays.
So what i discovered was that i can apply the array_walk function to the super globals. Doing so you automatically run a function call through the super globals
With this piece of code i wrote you should be able to secure most of you input data.
<?php
class secure
{
function secureSuperGlobalGET(&$value, $key)
{
$_GET[$key] = htmlspecialchars(stripslashes($_GET[$key]));
$_GET[$key] = str_ireplace("script", "blocked", $_GET[$key]);
$_GET[$key] = mysql_escape_string($_GET[$key]);
return $_GET[$key];
}
function secureSuperGlobalPOST(&$value, $key)
{
$_POST[$key] = htmlspecialchars(stripslashes($_POST[$key]));
$_POST[$key] = str_ireplace("script", "blocked", $_POST[$key]);
$_POST[$key] = mysql_escape_string($_POST[$key]);
return $_POST[$key];
}
function secureGlobals()
{
array_walk($_GET, array($this, 'secureSuperGlobalGET'));
array_walk($_POST, array($this, 'secureSuperGlobalPOST'));
}
}
?>
Note that you can modify this in anyway to suit your needs.
The Script has been tested.
An anonymous comment below suggests using PEAR with prepare() / execute() - though it was posted 3 years ago, it is still true today, though it's even easier now since PDO is included in most distributions. For SET/WHERE clauses and INSERT statements, just prepare the query with question marks in place of the dynamic parameters, bind each value in, then execute it, and it'll do all of the escaping for you, rendering the query immune to injection. Dynamic substitution of ORDER BY or LIMIT clauses has to be done the old fashioned way, though, so you still need to be careful with those.
Even without PDO, if you're using Postgres, you've already got the means to use parameterized queries, and if you're using MySQL, you simply need to ignore the outdated "mysql" extension and use "mysqli" instead.
The best prevention is to deactivate master..xp_cmdshell.
Run in isql the command `sp_configure 'xp_cmdshell''
If the value for "config value" is 1 then make in the isql
`sp_configure 'xp_cmdshell',0'.
If you want see the options from your mssql-Server make
`sp_configure 'show advanced options',1'
and then `sp_configure'
dark dot avenger at email dot cz wrote:
"I think that easy way to protect against SQL injection is to convert inputted data into binary format, so that whatever input is, in sql query it will consist only of 1s and 0s."
Unless there is a 1-to-1 correspondence between your inputted data and the characters in your 'binary' format, a SELECT query wouldn't work anymore. Not a binary format, but it makes my point: MIME encoding the text 'Dark Avenger' results in 'RGFyayBBdmVuZ2Vy'. If I wanted to look up anyone with 'Avenger' in his/her name, then 'Avenger' would be encoded as 'QXZlbmdlcg==' which clearly wouldn't result in a hit on 'RGFyayBBdmVuZ2Vy'.
If there IS a 1-to-1 correspondence, then EITHER your solution only makes it a bit harder to perform a SQL injection (a hacker would have to figure out what mapping was used between the text and the 'binary' format), OR you've come up with simply another way to escaping your data. Either isn't a terribly good solution to the SQL injection problem.
I think that easy way to protect against SQL injection is to convert inputted data into binary format, so that whatever input is, in sql query it will consist only of 1s and 0s.
Note that PHP 5 introduced filters that you can use for untrusted user input:
http://us.php.net/manual/en/intro.filter.php
This is a very helpful document from the MySQL site (in .pdf format) :
http://dev.mysql.com/tech-resources/articles/
guide-to-php-security-ch3.pdf
If you use the PEAR package and prepare() / execute() your queries,
you will hardly have to worry about any of this. Of course, it's still
a good idea to make sure you're putting valid data in your database...
