Web システムの脆弱性を開発者の視点に立ってわかりやすく説明する、その3です。
今回解説するのは以下の内容となります。
・機密情報・認証情報の漏えいによる脆弱性
・エラーメッセージ出力による機密情報の漏えい
・認証情報の不十分な保護
・不適切なパーミッションによる脆弱性
・不適切なパーミッション設定
参考: 不正なリクエストデータに対する脆弱性
参考: 認証の欠如による脆弱性
参考: 不正なメモリ領域へのアクセスによる脆弱性
参考: Web システムの脆弱性の分類
機密情報・認証情報の漏えいによる脆弱性
開発に携わる人(従業員や契約社員など)は当然のことながら機密情報や認証情報に触れる機会が多いですが、意図せず内部の人間から情報が漏えいするケースが年々増えてきています。
特にエラーメッセージなどのデバッグ情報からの情報漏えいには十分に注意しましょう。
一番やっかいなのは悪意のある内部の人間の犯行による個人情報の流出です。
被害も甚大であり、内部事情に精通した者が犯行を隠蔽するため見つけることが非常に困難です。
開発者だからすべてが見えていいというわけではなく、仕組みを作る人、環境を構築する人、実データを設定する人をきちんと分類し、それぞれが適切な参照範囲を保てるようにファイルの配置先やアクセス権限、環境構築手順等をしっかりと設計しましょう。
特にこれらの情報が含まれるリソースへのアクセスは常時モニタリングするなどの対策が必要になるかと思います。
エラーメッセージ出力による機密情報の漏えい
・情報漏えい(CWE-200 [jvndb])
[対象となるシステム]
すべてのシステム。
[脆弱性・攻撃方法]
エラーが発生したときに出力するエラーメッセージやデバッグ情報には、意図的にあるいは意図せず機密情報が含まれる場合があります。
・システム情報(OS 情報、ミドルウェア名、ライブラリパッケージ名など)
・プログラム情報(ソースコードのファイル名やパス名、エラー発生行など)
・エラーの詳細内容(エラーコード、対象リソース、対象者の個人情報、オペレーション履歴など)
これらの情報はごく単純に個人情報の漏えいに繋がるほか、攻撃者にとっての攻撃材料にもなり得るため、本脆弱性の影響度は広範囲に渡ります。
もちろん通常のコンテンツ内にもこのような情報が含まれてしまわないよう注意する必要があります。
以下は POST データを元にログインの判定を行う PHP のプログラム例です。
ユーザ名を間違えた場合とパスワードを間違えた場合で出力するメッセージが異なるため、攻撃者はユーザ名だけの総当たり検証が可能となります。
$username = $_POST['username'];
$password = $_POST['password'];
if(myfunc_IsValidUsername($username)){
if(myfunc_IsValidPassword($username, $password)){
print "Login Successful";
}else{
print "Login Failed - incorrect password";
}
}else{
print "Login Failed - unknown username";
}
以下は mysqli
を利用した場合の接続時のワーニングメッセージです。
接続ユーザ名やプライベート IP アドレス、ソースコードの場所や行など多くの機密情報が含まれていますので扱いには注意が必要です。
Warning: mysqli::__construct(): (HY000/1045): Access denied for user 'dbuser'@'192.168.10.16' (using password: YES) in /var/www/html/path/to/database.inc on line 4
以下は POST データからユーザ情報と銀行情報を取得する PHP のプログラム例です。myfunc_getBankAccount()
に失敗した場合に$user
がダンプされますが、$user
の中にある個人情報もすべてそのまま出力されてしまいます。
$username = $_POST['username'];
$accountNo = $_POST['accountNo'];
$user = null;
$bankAccount = null;
try{
$user = myfunc_getUser($username);
$bankAccount = myfunc_getBankAccount($user->id, $accountNo);
}catch(Exception $e){
echo $e->getMessage();
var_dump($user);
}
[対処方法]
利用者に見せるエラーメッセージと開発者が見るデバッグ情報は明確に分けなければなりません。
エラーメッセージではエラーの詳細原因には触れず、主に利用者の次のオペレーションを促す情報を表示します。
デバッグ情報は画面上には表示させず、開発者だけが見ることのできる場所にエラーログを出力するようにしましょう。
また、意図せずミドルウェアやライブラリのエラーメッセージが表示されてしまう場合があるため、テスト工程等で十分に確認を行いましょう。
例えば PHP ではエラーの画面表示やログ出力をphp.ini
やini_set()
等の設定で制御できます。
display_errors = Off # エラーの画面出力
log_errors = On # エラーのログ出力
なお、デバッグ情報にはなるべく利用者の個人情報やシステムのセキュリティ情報を含めないように心がけることが重要です。
どうしてもこれらの情報を含めざるを得ない場合には、デバッグ情報の出力先に注意が必要です。
同じ開発側の人間であっても、その情報を見ていい人とそうでない人を正しく把握して出力先を決定しましょう。
認証情報の不十分な保護
・ハードコードされた認証情報の使用(CWE-798 [jvndb])
・認証情報の不十分な保護(CWE-522 [jvndb])
[対象となるシステム]
認証の必要なシステム、また認証の必要なシステムやミドルウェアにアクセスするシステム。
[脆弱性・攻撃方法]
パスワードや暗号化キーなどの認証情報がハードコードされている場合、このコードにアクセスできる者はこれらの認証を容易に突破することができます。
ハードコードした認証情報が漏えいしてしまった場合にプログラムの修正が必要となりますが、クライアント配布型のパッケージであれば影響範囲は大きいでしょう。
このほか、設定ファイルの場所や暗号化アルゴリズムなどの情報から認証情報が割り出されてしまうケースも考えられます。
以下は PHP のフレームワーク Laravel の.env
ファイルの内容です。
DB への接続情報がすべてわかります。
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=My_DB
DB_USERNAME=dbuser
DB_PASSWORD=dbusrpassword
以下はプロパティファイルを読み込み、パスワードを取得する PHP のプログラム例です。
このプロパティファイルにアクセスできる者はパスワードも取得することができます。
Properties prop = new Properties();
prop.load(new FileInputStream("config.properties"));
String password = prop.getProperty("password");
DriverManager.getConnection(url, usr, password);
以下は入力されたパスワードを検証する Java のプログラム例です。
暗号化に圧縮アルゴリズムが使用されているため、容易に復号することができます。
int verifyAdmin(String password){
if(passwd.Equals(compress(password), compressed_password)){
return 0;
}
//Diagnostic Mode
return 1;
}
[対処方法]
同じ開発側の人達からこれらの情報を隠すということはなかなか難しい課題ではありますが、少なくともパスワードなどのセキュリティ情報の部分とプログラムの部分は分けるなどの工夫はすべきでしょう。
そして処理を実装する開発者と個々の本番環境へ固有値を適用する管理者を分け、この権限の境界をまたぐことができる人をきちんと管理することが重要かと思います。
例えば先ほどの Laravel の例でいうと、.env
ファイルはソース管理の対象から外しておき、デプロイしても本番環境に適用されないようにします。
その後しかるべき担当者が本番環境構築時に.env
の設定を行い、ファイルのアクセス権限などにより通常の開発者からはアクセスできないようにします。APP_ENV
による本番環境と開発環境の切り替えも設計段階からしっかりと考慮して実装しておくべきでしょう。
不適切なパーミッションによる脆弱性
前章でも言いましたが、開発者だからすべてが見えていいというわけではありません。
機密情報は最小特権の原則【PoLP】に基づいて、それが許可されている人のみアクセス可能にすべきであり、安全な領域に配置し信頼境界の外に出ることを許可してはいけません。
適切なファイルのアクセス権限を付与することは、コマンドインジェクションやパス・トラバーサルの対策にもなり得ます。
不適切なパーミッション設定
・不適切なデフォルトパーミッション(CWE-276 [jvndb])
・重要なリソースに対する不適切なパーミッションの割り当て(CWE-732 [jvndb])
[対象となるシステム]
すべてのシステム。
[脆弱性・攻撃方法]
必要以上に広範囲なアクセス権を付与すると、機密情報が公開されたり意図しない第三者によってそのリソースが変更されたりする可能性があります。
以下のコードは新しいユーザのホームディレクトリを作成し、そのユーザをディレクトリの所有者にします。
function myfunc_createUserDir($username){
$path = '/home/'.$username;
if(!mkdir($path)){
return false;
}
if(!chown($path,$username)){
rmdir($path);
return false;
}
return true;
}
mkdir()
の引数mode
が省略されているため、ディレクトリはデフォルトのアクセス許可0777
で作成されます。
すべてのユーザがディレクトリの読み取りと書き込みを行うことができるため、home
ディレクトリとしては適切ではありません。
[対処方法]
機密情報・認証情報が含まれているリソースには適切なアクセス権限を付与します。
特にファイル作成時のデフォルトのパーミッションをそのままにせず、後から適切なアクセス権を設定しましょう。
以下は 認証情報が記述されたプロパティファイルのアクセス権設定の例です。
-rw------- 1 root 13 Nov 24 17:58 config.properties
悪意のある内部の人間が一般ユーザでサーバにアクセスしても中身を見ることはできません。
またファイルの所有者が Web サーバの実行ユーザと異なるため、コマンドインジェクションやパス・トラバーサルを利用してファイルを編集することもできません。
続きます。
Web システムの脆弱性を開発者の視点に立ってわかりやすく説明する その1
Web システムの脆弱性を開発者の視点に立ってわかりやすく説明する その2
Web システムの脆弱性を開発者の視点に立ってわかりやすく説明する その4
コメント