ニッポンの素敵なプログラマーのmilです。
先日、セキュリティについて学ぶことがあったので備忘録としてまとめます。
XSS、SQLインジェクション、CSRFについてまとめています。
XSS
クロスサイトスクリプティングの略。
不正なリンクを第三者に踏ませることで不正なスクリプトが実行される脆弱性です。
リンクパラメータを引数に何らかの処理を実行するWebサイトやプログラムで、パラメータにJS等のブラウザで実行できるスクリプトを書くことで第三者に改ざんされたWebページを表示させることができたり、ブラウザに保管されたヘッダー情報からスクリプトを混入させたりできます。
※リンクパラメータとは
URLに入れられる変数みたいなもの。?
の後に変数名を書き=
で値を代入すると、リンク先でその値を変数名で取得できます。
&
を付けることで複数のパラメータを記述できます。
Googleの検索例ではq
という変数にqiita
という値が入っています。
https://www.google.com/search?q=qiita&rlz=1C1FQRR_jaJP939JP939&ei=rhCBYK-jE8aHoAS5rKKgAw&oq=qiita&gs_lcp=Cgdnd3Mtd2l6EAMyAggAMgIIADICCAAyAggAMgIIADICCAAyAggAMgIIADoHCAAQRxCwAzoKCAAQsQMQsQMQQzoICAAQsQMQsQM6BAgAEAQ6BQgAELEDOgcIABCxAxAEOgYIABAKECo6BAgAEEM6BwgAELEDEENQu35YqJsBYIqdAWgEcAJ4AIABWYgBtAiSAQIxM5gBAKABAaoBB2d3cy13aXqwAQDIAQjAAQE&sclient=gws-wiz&ved=0ahUKEwivsp-GlpHwAhXGA4gKHTmWCDQQ4dUDCA8&uact=5
この脆弱性を使い、攻撃を仕掛ける人はパラメータにスクリプトを書いて、第三者がそのリンクを踏むと、パラメータに書かれた処理が踏んだPCで実行されます。
http://mil.com?name=<script>何らかの処理</script>
対策
エスケープ処理を行いましょう。
パラメータに不正なスクリプトが記述された場合、そのままではブラウザが実行してしまうので、エスケープ処理を行い「実行可能なスクリプトが無効化された文字列」として認識されるようにします。
※エスケープとは
本来の使いかたは文章中に<>
などの記号を使う際、ブラウザでは特殊文字という扱いになっており<>
はタグを記述する際に使用する記号なので、文章中に<>
が使われると表示がおかしくなってしまいます。
ブラウザやプログラムにおいてこれらの記号が使いたいとき、実体参照というものを使い<>
を<>
と書くことで、文章中でも<>
を使用することができます。
これをパラメータで受け取る部分に使用することで、不正なスクリプトをただの文字列として表示することができます。
エスケープ処理はさまざまな言語で実装されています。
この記事では、私が良く使うJSとPHPを例にします。
escape()
htmlspecialchars ( string $string , int $flags = ENT_COMPAT , string|null $encoding = null , bool $double_encode = true )
SQLインジェクション
そのままの通り、SQLを混入させて不正な操作をする脆弱性です。
ユーザーから入力された値を元にSQLを組み立てる処理を行うとき、プリペアドステートメントを使用していないと、不正なSQL文などが含まれる値を入力されたときに、意図しないデータが出力されます。
対策
プリペアドステートメントを使用し「実行可能なSQLを無効化した文字列」として扱いましょう。
※プリペアードステートメントとは
基本的にはXSSでも出てきたエスケープ処理と同じです。
IPAの説明では
プリペアドステートメントを利用すると、入力データは、数値定数や文字列定数として組み込まれるため、特殊記号が含まれていた場合でも、それはただの文字として扱われることになる。
と記されており、もしSQL文を含む値が入力されたとしても、プログラムはSQLではなく、単純な文字列として認識して実行されます。
プリペアドステートメントは主要な言語はほぼ全て実装されています(外部ライブラリで実装するものもある)。
connection.query('insert into hoge(NAME) values(?),(?),(?)', ['fuga','fuga','fuga'],function(error,results,fields){
});
例の引用
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
$name = 'one';
$value = 1;
$stmt->execute();
CSRF
クロスサイトリクエストフォージェリの略です。
ユーザーが使うサービスにログイン状態のまま、他人が作成した悪意あるURLを踏んでしまうことで、意図しない処理が行われる脆弱性です。
これは少しXSSと似ています。
XSSでは、URLのリンクパラメータに不正なスクリプトを入力することによって攻撃される脆弱性でした。
http://mil.com?name=<script>何らかの処理</script>
ユーザーを扱うWebサービスでは、ユーザーの情報を変更する際、このようなリクエストをサーバーへ送信するURLを立てることがあります。
http://mil.com/login/user?changename=micchili
これを悪用します。攻撃者はもちろん、ユーザーのアカウント情報は知らないのでログインすることができず、このリクエストを飛ばせるページを開くこともできません。
しかし、ログインしているユーザーはこのリクエストを送ることができます。
なので、攻撃者はchangnameの部分だけ変更したURLを作成し、ユーザーに踏ませます。
http://mil.com/login/user?changename="Fワードやイケナイ単語"
このURLをもしログイン済みのユーザーが踏んでしまった場合、名前が勝手に書き換わってしまいます。
このような、サービスにログイン済みのユーザーを狙った脆弱性がCSRFです。
対策
ログイン後のページに毎回変更される推測不可能なトークン(ページトークン)を生成する。
最近ではSPAと呼ばれるJSで構成されるWebサイトが増えましたが、大規模なWebサービスなどはサーバーからHTMLを返す方法で構築されています。その方法では、基本的にプログラムで動的にHTMLを生成することができるので、そこでトークンを発行することで、第三者が作ったURLをログイン済みのユーザーが踏んでしまっても、トークンで本当にユーザーのリクエストなのかを照合できます。
こちらのサイトが理解しやすいと思います
感想
Webサイトを作るうえで、初歩的な攻撃対策をもう一度復習できてよかったと思いました。
昨今ではフレームワークやライブラリでほとんど意識せずこういったセキュリティは対策されていますが、なぜその対策を行うのか、なぜこの関数を必ず使わなければならないのかといった、テンプレートを使う上で疑問に思うものが解消できました。
セキュリティの脆弱はサービスの存亡に関わる非常に重要なものなので、今後も慢心せず学んでいきたいと思います。