1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
|
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision: 1.20 $ -->
<chapter id="security">
<title>セキュリティ</title>
<simpara>
PHP は強力な言語そしてインタプリタであり、モジュールとして Web サー
バーに組み込んだ場合でも、独立した <acronym>CGI</acronym> バイナリ
として実行される場合でも、ファイルをアクセスしたり、コマンドを実行
したり、サーバーへのネットワーク接続を開くことができます。デフォル
トでは、これらの機能を実行した場合、Webサーバー上でセキュリティ上の
問題を生じる可能性があります。PHP は、特に CGI プログラムを書く場合、
Perl や C より安全な言語となるように設計されています。コンパイル時
または実行時の設定オプションを正しく選び、適切なコードを書くことに
より、必要な自由度とセキュリティの組み合わせを確実に提供することが
できます。
</simpara>
<simpara>
PHP の使用法に多くの異なった手段があるように、PHP の動作を制御する
多くの設定オプションがあります。オプションの選択肢が広いため、PHP
を様々な用途に使用することができます。しかし、このことは、これらの
オプションとサーバー設定の組み合わせによっては、安全でない設定とな
ることを意味します。
</simpara>
<simpara>
PHPの設定の自由度はそのコードの柔軟さにほぼ匹敵します。PHPは、シェ
ルユーザコマンドを全て実行可能な完全なサーバーアプリケーションや
厳しく制御された環境で低リスクの簡単なサーバーサイドインクルードを
使用できるようなアプリケーションを構築する場合に使用することが可能
です。そうした環境の構築方法、セキュリティのレベルはPHPの開発者に大
きく依存しています。
</simpara>
<simpara>
本章は、安全に使用可能な異なった設定と条件の組み合わせについての説
明から始めます。続いて、複数のセキュリティレベルのコーディングにお
ける複数の考慮事項について説明し、最後にいくつかの一般的なセキュリ
ティ上のアドバイスを行います。
</simpara>
<sect1 id="security.general">
<title>一般的な考慮事項</title>
<simpara>
完全に安全なシステムは理想の産物でしかないため、セキュリティ業界で
しばしば使用される手法は、リスクと利便性のバランスのとれた手法です。
変数がユーザから投稿される度に(網膜スキャンと指紋のような)2種類の
生体認証が必要だとしたら、極端に高いレベルの説明義務を生じます。ま
た、かなり複雑なフォームを埋めるために30分もかかるとすれば、ユーザ
がセキュリティをバイパスする手段を探す気分にさせる傾向があります。
</simpara>
<simpara>
最善のセキュリティは、通常、ユーザによる業務の達成を防たげずに要求
を十分に達成できる程度にさしでがましくないものです。むしろ、いくつ
かのセキュリティ攻撃は、単純にこの種の多げさに構築され、時間を浪費
しがちなセキュリティ機構を狙うものです。
</simpara>
<simpara>
記憶するに値する言葉として次のようなものがあります。「システムは鎖
の最も弱い輪と同程度に優れている」全てのトランザクションが時間、場
所、トランザクションの型等に基づき大量に記録されているが、ユーザは
一つのクッキーのみにより認証されている場合、ユーザとそのトランザク
ションログの結び付きの確実性はかなり弱くなります。
</simpara>
<simpara>
テストの際に、最も簡単なページに関してでさえ、全ての可能性をテスト
することは不可能であるということを頭に入れておいて下さい。期待する
入力は、不機嫌な社員、経験のあるクラッカー、キーボードの上を歩く家
の猫による入力とは全く無関係でしょう。これが、想定外のデータが入力
される可能性がある場所を見分けるために論理的な視点からコードを見て、
その後、修正、減少、または詳細に調べるというのが、最善であるという
理由です。
</simpara>
<simpara>
インターネットにはあなたのコードを壊したり、システムを破壊したり、
不適切な内容を投稿したり、その他あなたの一日を不快にするようなこと
により自分の名を馳せたいと思う人がたくさんいます。サイトの規模の大
小によらず、単にオンラインであり、接続できるサーバを有しているだけ
で攻撃目標となりえます。多くのクラック用プログラムはサイトの大きさ
を考慮せず、犠牲者を探しつつ大きなIPブロックで網を張っています。
</simpara>
</sect1>
<sect1 id="security.cgi-bin">
<title>CGI バイナリとしてインストール</title>
<sect2 id="security.cgi-bin.attacks">
<title>有りうる攻撃</title>
<simpara>
PHP を <acronym>CGI</acronym> バイナリとして使用するのは、PHP を
モジュールとして(Apache のような)サーバーソフトウエアに組み込み
たくない何らかの理由がある場合や安全な chroot と setuid 環境をス
クリプトに提供する他の CGI ラッパーと組み合わせて PHP を使用する
場合の設定オプションです。セットアップ時には、通常、PHP 実行バイ
ナリを Web サーバーの cgi-bin ディレクトリにインストールします。
CERT 勧告 <ulink url="&url.cert;">CA-96.11</ulink>は、いかなるイ
ンタプリタを cgi-bin に置くことにも反対しています。
PHP バイナリをスタンドアロンのインタプリタとして使用することが
できる場合でも、PHP は、セットアップにより生じる可能性がある
次のような攻撃を防ぐように設計されています。
</simpara>
<itemizedlist>
<listitem>
<simpara>
システムファイルへのアクセス: <filename
role="url">http://my.host/cgi-bin/php?/etc/passwd</filename>
</simpara>
<simpara>
URL において疑問符 (?) の後のクエリー情報は、CGI インターフェー
スにより、インタプリタにコマンドライン引数として渡されます。通
常、インタプリタは、コマンドライン上の最初の引数に指定されたファ
イルを開き、実行します。
</simpara>
<simpara>
CGI バイナリとして実行された場合、PHP は、コマンドライン引数の
解釈を拒否します。
</simpara>
</listitem>
<listitem>
<simpara>
サーバー上の Web ドキュメントへのアクセス: <filename
role="url">http://my.host/cgi-bin/php/secret/doc.html</filename>
</simpara>
<simpara>
URL の PHP バイナリ名の後のパス情報の部分、つまり<filename
role="uri">/secret/doc.html</filename> は、
<acronym>CGI</acronym> プログラムによりオープンされて実行される
ファイルの名前を指定するために従来より使用されています。
<filename role="url">http://my.host/secret/script.php</filename>
のようなドキュメントへの要求を PHP インタプリタにリダイレクト
するために、通常、何らかの Web サーバー設定用命令(Apache では Action)
が使用されます。この設定により、Web サーバーは、まずディレクトリ
<filename role="uri">/secret</filename> へのアクセス権をチェックし、
リダイレクト要求 <filename
role="url">http://my.host/cgi-bin/php/secret/script.php</filename>
を生成します。残念なことに、リクエストが最初からこの形式で与え
られた場合、Web サーバーによるアクセスチェックは、
<filename role="uri">/secret/script.php</filename> ファイル
ではなく、<filename role="uri">/cgi-bin/php</filename> ファイル
に対して行われます。この手法により、<filename
role="uri">/cgi-bin/php</filename> にアクセス可能なユーザーは、
Web サーバー上の全ての保護されたドキュメントにアクセスできてし
まいます。
</simpara>
<simpara>
PHP では、サーバードキュメントツリーにアクセス制限付きのディレ
クトリがある場合、コンパイル時の設定オプション <link
linkend="install.configure.enable-force-cgi-redirect">
--enable-force-cgi-redirect</link> および実行時の設定命令
<link linkend="ini.doc-root">doc_root</link> と
<link linkend="ini.user-dir">user_dir</link> をこの攻撃を防止す
るために使用することができます。 これらを組み合わせたいくつか
の手法について以下に詳細な説明を示します。
</simpara>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="security.cgi-bin.default">
<title>ケース 1: 公開ファイルのみを配布</title>
<simpara>
サーバー上にパスワードまたは IP アドレスを元にしたアクセス制限に
よる制約を受けるコンテンツがない場合、この設定オプションを使用す
る必要はありません。使用する Web サーバーがリダイレクトを許可しな
い場合やサーバーがリダイレクト要求を安全に処理しつつPHP バイナリ
と通信できる手段を有していない場合、オプション <link
linkend="install.configure.enable-force-cgi-redirect">
--enable-force-cgi-redirect</link>を
configure スクリプトに指定することができます。この場合でも、直接
的な方法 <filename
role="php">http://my.host/cgi-bin/php/dir/script.php</filename>
でもなくリダイレクション <filename
role="php">http://my.host/dir/script.php</filename>でもない他の
やり方で PHP スクリプトを呼び出せるようになっていないかどうか確認
する必要があります。
</simpara>
<simpara>
リダイレクションは、例えば Apache では命令 AddHandler および
Action で設定することができます。(以下を参照してください。)
</simpara>
</sect2>
<sect2 id="security.cgi-bin.force-redirect">
<title>ケース 2: --enable-force-cgi-redirect を使用</title>
<simpara>
このコンパイル時のオプションは、
<filename role="php">http://my.host/cgi-bin/php/secretdir/script.php
</filename> のように URL から直接 PHP を呼び出すことを禁止します。
代わりに、
Web サーバーのリダイレクションにより処理された場合は、
PHP はこのモードでのみ処理を行います。
</simpara>
<simpara>
通常、Apache 用設定でのリダイレクションは、
次の命令を使用して行います。</simpara>
<programlisting role="apache-conf">
<![CDATA[
Action php-script /cgi-bin/php
AddHandler php-script .php
]]>
</programlisting>
<simpara>
このオプションは、Apache Web サーバーでのみテストされており、リク
エストのリダイレクト時に Apache が標準ではないCGI 環境変数
<envar>REDIRECT_STATUS</envar> をセットすることを前提にしています。
リクエストが直接のものであるか間接のものであるかを示す手段をWeb
サーバーが全くサポートしていない場合は、このオプションを使用する
ことはできません。この場合、ここで記した CGI 版を実行する他の方法
の内の一つを使用する必要があります。
</simpara>
</sect2>
<sect2 id="security.cgi-bin.doc-root">
<title>ケース 3: doc_root または user_dir を設定</title>
<simpara>
Web サーバー上のドキュメントディレクトリに
スクリプトや実行ファイルのようなアクティブな内容を読み込むのは、
往々にして危険な行為であるとみなされることがあります。
何らかの設定ミスによりスクリプトが実行されず、通常の HTML ドキュメント
として表示されてしまう場合には、知的著作物またはパスワードのような
セキュリティ情報が漏洩する可能性があります。
このため、多くのシステム管理者は、スクリプトを PHP CGI を通じてのみ
アクセス可能な他のディレクトリ構造にセットアップしたいと思うこと
でしょう。
この場合、常にインタープリタに処理されるため、上記のように表示されること
はありません。
</simpara>
<simpara>
前節で記したようなリクエストがリダイレクトされたものでないことを
確かめる方法が利用可能でない場合、
スクリプト用の doc_root を Web ドキュメント用ルートとは別に
セットアップする必要があります。
</simpara>
<simpara>
設定用命令 <link linkend="ini.doc-root">doc_root</link> により
<link linkend="configuration.file">設定ファイル</link> ファイル中で
PHP スクリプト用ドキュメントルートを設定することができます。
または、環境変数 <envar>PHP_DOCUMENT_ROOT</envar> でも設定する
ことができます。
これを設定した場合、CGI 版の PHP は、
常に開くファイルの名前をこの <parameter>doc_root</parameter>
リクエストのパス情報を用いて作成し、
(以下の <parameter>user_dir</parameter> を除き、)確実に
このディレクトリの外側でスクリプトが実行されないようにします。
</simpara>
<simpara>
ここで利用可能な別のオプションは、<link linkend="ini.user-dir">
user_dir</link> です。user_dir が設定されていない場合、
開かれるファイル名を制御するのは、<parameter>doc_root</parameter>
のみです。
<filename role="url">http://my.host/~user/doc.php</filename> のような
URL は、ユーザーホームディレクトリ以下のファイルを開かず、
doc_root 以下の <filename role="uri">~user/doc.php</filename>
というファイルを開くことになります。
(ディレクトリ名がチルダ [<literal>~</literal>] で始まっている
ということになります)
</simpara>
<simpara>
user_dir が例えば、<filename role="dir">public_php</filename>に
設定されていた場合、
<filename role="url">http://my.host/~user/doc.php</filename> の
ようなリクエストは、そのユーザー user のホームディレクトリにある
<filename role="dir">public_php</filename> 以下の
<filename>doc.php</filename> という名前のファイルをオープンしま
す。ユーザーのホームディレクトリが、
<filename role="dir">/home/user</filename> である場合、
実行されるファイルは、
<filename>/home/user/public_php/doc.php</filename>
となります。
</simpara>
<simpara>
<parameter>user_dir</parameter> の展開は、
<parameter>doc_root</parameter> の設定によらず行われます。
このため、ドキュメントルートおよびユーザーディレクトリへの
アクセスを別々に制御することができます。
</simpara>
</sect2>
<sect2 id="security.cgi-bin.shell">
<title>ケース 4: web ツリーの外に PHP パーサを置く</title>
<para>
非常に安全性の高いオプションとしてPHP パーサのバイナリをファイル
用 Web ツリーの外側、例えば <filename
role="dir">/usr/local/bin</filename>に置くことが考えられます。こ
のオプションの唯一の欠点は、PHP タグを有する全てのファイルの先頭
行に次のような一行を加える必要があることです。
<informalexample>
<programlisting>
<![CDATA[
#!/usr/local/bin/php
]]>
</programlisting>
</informalexample>
また、ファイルを実行可能にしておく必要があります。この場合、実行
時にシェルエスケープ機能 <literal>#!</literal> を使用する Perl や
sh や他のスクリプト言語で書かれた CGI スクリプトを処理するのと全
く同様に処理を行います。
</para>
<para>
この設定で <envar>PATH_INFO</envar> および
<envar>PATH_TRANSLATED</envar> 情報を正しく処理するためには、
PHP パーサを設定オプション
<link linkend="install.configure.enable-discard-path">
--enable-discard-path</link> を付けてコンパイルする必要があります。
</para>
</sect2>
</sect1>
<sect1 id="security.apache">
<title>Apache モジュールとしてインストール</title>
<simpara>
PHP が Apache モジュールとして使用された場合、PHP は、Apache ユー
ザーの許可属性(通常はユーザー "nobody" の許可属性)を継承します。
これは、セキュリティと認証に数々の影響を与えます。例えば、データベー
スと接続するためにPHPを使用している場合、データベースが組込みのア
クセス制御機能を有していない限り、そのデータベースを "nobody"ユー
ザからアクセス可能とする必要が生じます。これは、悪意のあるスクリプ
トが、ユーザ名とパスワードなしにデータベースにアクセスし、修正する
ことができることを意味します。Webスパイダがデータベース管理用Webペー
ジを回って、データベースを全て削除することも可能です。Apache認証に
よりこの攻撃に対して防衛することが可能であり、また、LDAPや
.htaccessファイル等を使用して固有のアクセスモデルを設計し、PHPスク
リプトの一部としてそのコードを読み込むことも可能です。
</simpara>
<simpara>
しばしば、PHPユーザ(この場合はApacheユーザ)が非常に小さなリスクを
有する場所に一度セキュリティが確立されると、PHPはユーザディレクト
リにウイルスファイルを書き込んだりすることができなくなります。もし
くは、データベースにアクセスしたり変更したりといったことが出来なく
なります。この場合、良いファイルおよび悪いファイルの書き込み、また
は、良いデータベーストランザクションと悪いデータベーストランザクシ
ョンに関して等しく安全性が確保されていると言えます。
</simpara>
<simpara>
この観点からしばしば行われるセキュリティ上の失敗としてApacheにルー
ト権限を与えたり、他の何らかの手段でApacheの権限を昇格させるという
ものがあります。
</simpara>
<simpara>
Apacheユーザの権限をルートに昇格させることは非常に危険であり、シ
ステム全体を危険にさらす可能性があります。よって、sudoやchrootの実
行、ルート権限で実行を行う他の手段は、セキュリティに精通した人以外
は、考慮するべきではありません。
</simpara>
<simpara>
いくつかのより簡単な解決策があります。
<function>open_basedir()</function> を使用することにより、PHPに使用
を許可するディレクトリを制御したり制限したりすることが可能です。
また、全てのWebベースの作業をユーザファイル、システムファイル以外の
ファイルに制限するために、Apache専用エリアを設定することも可能です。
</simpara>
</sect1>
<sect1 id="security.filesystem">
<title>ファイルシステムのセキュリティ</title>
<simpara>
PHP は、ファイルおよびディレクトリ毎に権限を設定する多くのサーバシ
ステム上に組み込まれたセキュリティを提供します。これにより、ファイ
ルシステム内のファイルを読み込み可能に制御することが可能になります。
全てのファイルは世界中から読み込み可能であり、このファイルシステム
にアクセスした全てのユーザから読み込まれても安全であることを確認す
る必要があります。
</simpara>
<simpara>
PHPは、ファイルシステムにユーザレベルのアクセスを許可するように設
計されているため、PHPスクリプトから/etc/password のようなシステム
ファイルを読み込み可能としたり、イーサネット接続を修正したり、巨大
なプリンタジョブを出力したりすることができます。これから明らかにわ
かることですが、読み書きするファイルを適切に設定する必要があります。
</simpara>
<simpara>
各自のホームディレクトリにあるファイルを削除する次のスクリプトを見
てみましょう。これは、ファイル管理用にWebインターフェースを使用す
る場合に通常生じるような設定を仮定しています。この場合、Apacheユー
ザはそのユーザのホームディレクトリにあるファイルを削除可能です。
</simpara>
<para>
<example>
<title>甘い変数の確認から生じるリスク</title>
<programlisting role="php">
<![CDATA[
<?php
// ユーザのホームディレクトリからファイルを削除する
$username = $HTTP_POST_VARS['user_submitted_name'];
$homedir = "/home/$username";
$file_to_delete = "$userfile";
unlink ($homedir/$userfile);
echo "$file_to_delete は削除されました!";
?>
]]>
</programlisting>
</example>
usernameはユーザフォームから投稿可能であるため、usernameを投稿し、
他の誰かが所有するファイルを指定、削除することが可能です。この場合、
他の何らかの形式の認証を使用するべきです。投稿された変数が、
"../etc/" と "passwd " であった場合について考えてみましょう。簡単
なコードを以下に示します。
<example>
<title>... ファイルシステムへの攻撃</title>
<programlisting role="php">
<![CDATA[
<?php
// 外部からPHPユーザがアクセス可能なハードドライブを削除します。PHPが
// ルートのアクセス権限を有している場合、
$username = "../etc/";
$homedir = "/home/../etc/";
$file_to_delete = "passwd";
unlink ("/home/../etc/passwd");
echo "/home/../etc/passwd は削除されました!";
?>
]]>
</programlisting>
</example>
こうした問題を防止するために必要な重要なチェック手段として以下の2
種類のものがあります。
<itemizedlist>
<listitem>
<simpara>
PHP Webユーザバイナリに制限された権限のみを許可する。
</simpara>
</listitem>
<listitem>
<simpara>
投稿された全ての変数を確認する。
</simpara>
</listitem>
</itemizedlist>
以下に改良されたスクリプトを示します。
<example>
<title>より安全なファイル名の確認</title>
<programlisting role="php">
<![CDATA[
<?php
// PHPユーザがアクセス可能なハードドライブからファイルを削除する。
$username = $HTTP_SERVER_VARS['REMOTE_USER']; // 認証機構を使用する
$homedir = "/home/$username";
$file_to_delete = basename("$userfile"); // パスを取り除く
unlink ($homedir/$file_to_delete);
$fp = fopen("/home/logging/filedelete.log","+a"); // 削除の記録
$logstring = "$username $homedir $file_to_delete";
fputs ($fp, $logstring);
fclose($fp);
echo "$file_to_delete は削除されました!";
?>
]]>
</programlisting>
</example>
しかし、これでも、傷口を塞いだことにはなりません。
ユーザが自分用のユーザログインを作成することをあなたの認証システムが
許可しており、ユーザが"../etc/"へのログインを選択した場合、システム
はまたも公開されてしまいます。このため、よりカスタマイズされたチェッ
クを行なう方がよいでしょう。
<example>
<title>より安全なファイル名の確認</title>
<programlisting role="php">
<![CDATA[
<?php
$username = $HTTP_SERVER_VARS['REMOTE_USER']; // 認証機構を使用する
$homedir = "/home/$username";
if (!ereg('^[^./][^/]*$', $userfile))
die('bad filename'); // 処理せず、終了。
if (!ereg('^[^./][^/]*$', $username))
die('bad username'); // 処理せず、終了。
//etc...
?>
]]>
</programlisting>
</example>
</para>
<para>
オペレーティングシステムにより、注意するべきファイルは大きく変化し
ます。これらには、デバイスエントリ(/dev/ または COM1)、設定ファイ
ル(/etc/ ファイルおよび .ini ファイル)、よく知られたファイル保存領
域 (/home/、 My Documents)等が含まれます。このため、明示的に許可す
るもの以外の全てを禁止する方針とする方が通常はより簡単です。
</para>
</sect1>
<sect1 id="security.database">
<title>データベースのセキュリティ</title>
<simpara>
今日、ダイナミックなコンテンツを提供するウェブアプリケーションに
おいてはデータベースは欠く事のできなコンポーネントとなっています。
そういったデータベースには重要な、そして秘密にすべき情報が格納
されることになるので、それらをいかにして保護するかについて十分に
考慮する必要があります。
</simpara>
<simpara>
情報を取り出したり格納するためにはデータベースに接続する必要があります。
そして適切なクエリを送信し、結果を受け取り、切断します。クエリに
使用される言語はStructured Query Language (SQL)が一般的です。アタッカー
がどのように<link linkend="security.database.sql-injection">SQLに
干渉する</link>かについて参照してください。
</simpara>
<simpara>
皆さんがお気づきの様に、PHPそれ自体は貴方のデータベースを保護することは
ありません。以下のセクションはPHPスクリプトからどのようにデータベースに
アクセスし操作すればいいのか、とういことに関する非常に基本的な導入です。
</simpara>
<simpara>
このシンプルなルールを覚えて置いてください:それは「多重防衛」です。
より多くの箇所で、より多くの保護を行うことにより、アタッカーが攻撃に
成功して機密情報が漏洩する可能性は減っていきます。データベースと
アプリケーションを正しくデザインすることで貴方の心配を取り除くことが
できます。
</simpara>
<sect2 id="security.database.design">
<title>データベースのデザイン</title>
<simpara>
他人が用意した既存のものを使用するのでない限り、最初に行うのはデータベースの作成です。
データベースが作成されると、そのデータベースのオーナーは作成コマンドを
実行したユーザになります。通常、オーナー(とスーパーユーザー)のみが
そのデータベースに対して操作を行うことが出来ます。他のユーザがデータベースを
使用するには適切な権利が与えられている必要があります。
</simpara>
<simpara>
アプリケーションはデータベースにオーナー、もしくはスーパーユーザーとして
接続しては絶対にいけません。なぜならこれらのユーザは
例えばスキーマの変更(テーブルの削除等)や全コンテンツの削除、といった
あらゆるクエリーを実行することが出来るからです。
</simpara>
<simpara>
貴方が作成するアプリケーションがデータベースに対して行う操作の各方面ごとに、
捜査対象となるオブジェクトに対して、出来る限り少ない権限を持った複数の
ユーザを作成した方が良いでしょう。ユーザに対しては、最低限必要な権限のみを
与え、関係の無いデータへのアクセスを許可しないようにします。これは、
万が一侵入者がそのユーザの権限を以ってデータベースにアクセスした際に、
アプリケーションと関係の無いデータにまでアクセスされることを防ぐためです。
</simpara>
<simpara>
全てのビジネスロジックをウェブアプリケーション(つまり貴方のスクリプト)
で実装することは推奨されません。代わりに、ビュー、トリガー、ルールといった
データベースの機能を活用した方が良いでしょう。システムが更新され、
新しい機能がデータベースへのアクセスすることになった場合、個々のデータベース
クライアントごとに再度同様のロジックを実装しなければならなくなります。
さらに、トリガーは透過的に、そして自動的にフィールドを扱うことが出来るので、
デバッグ時や、トランザクションのロールバック時に役立ちます。
</simpara>
</sect2>
<sect2 id="security.database.connection">
<title>データベースへの接続</title>
<simpara>
更なるセキュリティのために、クライアント/サーバ間の通信においてSSLを用いた
暗号化を行った方が良いでしょう。もしくはsshを使用することも出来ます。
どちらかの手段を講じた後、トラフィックをモニタリングしてみれば
ここから何らかの情報を得ることが困難だという事が分かると思います。
</simpara>
<!--simpara>
If your database server native SSL support, consider to use <link
linkend="ref.openssl">OpenSSL functions</link> in communication between
PHP and database via SSL.
</simpara-->
</sect2>
<sect2 id="security.database.storage">
<title>暗号化記憶モデル</title>
<simpara>
SSL/SSHによってクライアント/サーバ間で通信されるデータは保護されますが、
データベースに保存されたデータは保護されません。SSLはあくまで通信上の
プロトコルなのです。
</simpara>
<simpara>
一旦アタッカーがデータベースへ(ウェブサーバを通さずに)アクセスできてしまうと、
そこに格納されているデータ自体が暗号化されていない限り、自由に閲覧され、
使用されてしまいます。データを暗号化することによって、この脅威を減らすことが
できますが、この機能をサポートしているデータベースは僅かです。
</simpara>
<simpara>
この問題への最も簡単な対応策は、まず自分専用の暗号化パッケージを作成し、
それをあなたのPHPスクリプトから使用することです。PHPの<link
linkend="ref.mcrypt">Mcrypt</link>, <link linkend="ref.mhash">Mhash</link>
といった幾つかの拡張モジュールは、様々な暗号化アルゴリズムをサポート
しているので役に立つでしょう。データ格納時に暗号化を行い、取得時に
復号化します。この方法についてはリファレンスを参照してください。
</simpara>
<simpara>
もし完全にデータを隠したい場合や、元のデータ自体は必要ない場合(つまり
表示されない場合)は、ハッシュも考慮に入れたほうが良いでしょう。
ハッシュの良く知られた使用方法は、パスワードをそのまま格納せずに、
そのMD5ハッシュ値を格納する方法です。<function>crypt</function>や
<function>md5</function>も参照してください。
</simpara>
<example>
<title>ハッシュパスワードフィールドを使う</title>
<programlisting role="php">
<![CDATA[
// ハッシュされたパスワードを格納する
$query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');",
addslashes($username), md5($password));
$result = pg_exec($connection, $query);
// パスワードが正しいかどうか問い合わせる
$query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';",
addslashes($username), md5($password));
$result = pg_exec($connection, $query);
if (pg_numrows($result) > 0) {
echo "Welcome, $username!";
}
else {
echo "Authentication failed for $username.";
}
]]>
</programlisting>
</example>
</sect2>
<sect2 id="security.database.sql-injection">
<title>SQL Injection</title>
<simpara>
Many web developers are unaware of how SQL queries can be tampered with,
and assume that an SQL query is a trusted command. It means that SQL
queries are able to circumvent access controls, thereby bypassing standard
authentication and authorization checks, and sometimes SQL queries even
may allow access to host operating system level commands.
</simpara>
<simpara>
Direct SQL Command Injection is a technique where an attacker creates or
alters existing SQL commands to expose hidden data, or to override valuable
ones, or even to execute dangerous system level commands on the database
host. This is accomplished by the application taking user input and
combining it with static parameters to build a SQL query. The following
examples are based on true stories, unfortunately.
</simpara>
<para>
Owing to the lack of input validation and connecting to the database on
behalf of a superuser or the one who can create users, the attacker
may create a superuser in your database.
<example>
<title>
Splitting the result set into pages ... and making superusers
(PostgreSQL and MySQL)
</title>
<programlisting role="php">
<![CDATA[
$offset = argv[0]; // beware, no input validation!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
// with PostgreSQL
$result = pg_exec($conn, $query);
// with MySQL
$result = mysql_query($query);
]]>
</programlisting>
</example>
Normal users click on the 'next', 'prev' links where the <varname>$offset</varname>
is encoded into the URL. The script expects that the incoming
<varname>$offset</varname> is decimal number. However, someone tries to
break in with appending <function>urlencode</function>'d form of the
following to the URL
<informalexample>
<programlisting>
<![CDATA[
// in case of PostgreSQL
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select 'crack', usesysid, 't','t','crack'
from pg_shadow where usename='postgres';
--
// in case of MySQL
0;
UPDATE user SET Password=PASSWORD('crack') WHERE user='root';
FLUSH PRIVILEGES;
]]>
</programlisting>
</informalexample>
If it happened, then the script would present a superuser access to him.
Note that <literal>0;</literal> is to supply a valid offset to the
original query and to terminate it.
</para>
<note>
<para>
It is common technique to force the SQL parser to ignore the rest of the
query written by the developer with <literal>--</literal> which is the
comment sign in SQL.
</para>
</note>
<para>
A feasible way to gain passwords is to circumvent your search result pages.
What the attacker needs only is to try if there is any submitted variable
used in SQL statement which is not handled properly. These filters can be set
commonly in a preceding form to customize <literal>WHERE, ORDER BY,
LIMIT</literal> and <literal>OFFSET</literal> clauses in <literal>SELECT</literal>
statements. If your database supports the <literal>UNION</literal> construct,
the attacker may try to append an entire query to the original one to list
passwords from an arbitrary table. Using encrypted password fields is
strongly encouraged.
<example>
<title>
Listing out articles ... and some passwords (any database server)
</title>
<programlisting role="php">
<![CDATA[
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'
ORDER BY $order LIMIT $limit, $offset;";
$result = odbc_exec($conn, $query);
]]>
</programlisting>
</example>
The static part of the query can be combined with another
<literal>SELECT</literal> statement which reveals all passwords:
<informalexample>
<programlisting>
<![CDATA[
'
union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
--
]]>
</programlisting>
</informalexample>
If this query (playing with the <literal>'</literal> and
<literal>--</literal>) were assigned to one of the variables used in
<varname>$query</varname>, the query beast awakened.
</para>
<para>
SQL UPDATEs are also subject to attacking your database. These queries are
also threatened by chopping and appending an entirely new query to it. But
the attacker might fiddle with the <literal>SET</literal> clause. In this
case some schema information must be possessed to manipulate the query
successfully. This can be acquired by examing the form variable names, or
just simply brute forcing. There are not so many naming convention for
fields storing passwords or usernames.
<example>
<title>
From resetting a password ... to gaining more privileges (any database server)
</title>
<programlisting role="php">
<![CDATA[
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
]]>
</programlisting>
</example>
But a malicious user sumbits the value
<literal>' or uid like'%admin%'; --</literal> to <varname>$uid</varname> to
change the admin's password, or simply sets <varname>$pwd</varname> to
<literal>"hehehe', admin='yes', trusted=100 "</literal> (with a trailing
space) to gain more privileges. Then, the query will be twisted:
<informalexample>
<programlisting role="php">
<![CDATA[
// $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 ...;"
]]>
</programlisting>
</informalexample>
</para>
<para>
A frightening example how operating system level commands can be accessed
on some database hosts.
<example>
<title>Attacking the database host's operating system (MSSQL Server)</title>
<programlisting role="php">
<![CDATA[
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
]]>
</programlisting>
</example>
If attacker submits the value
<literal>a%' exec master..xp_cmdshell 'net user test testpass /ADD' --</literal>
to <varname>$prod</varname>, then the <varname>$query</varname> will be:
<informalexample>
<programlisting role="php">
<![CDATA[
$query = "SELECT * FROM products WHERE id LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--";
$result = mssql_query($query);
]]>
</programlisting>
</informalexample>
MSSQL Server executes the SQL statements in the batch including a command
to add a new user to the local accounts database. If this application
were running as <literal>sa</literal> and the MSSQLSERVER service is
running with sufficient privileges, the attacker would now have an
account with which to access this machine.
</para>
<note>
<para>
Some of the examples above is tied to a specific database server. This
does not mean that a similar attack is impossible against other products.
Your database server may be so vulnerable in other manner.
</para>
</note>
<sect3 id="security.database.avoiding">
<title>Avoiding techniques</title>
<simpara>
You may plead that the attacker must possess a piece of information
about the database schema in most examples. You are right, but you
never know when and how it can be taken out, and if it happens,
your database may be exposed. If you are using an open source, or
publicly available database handling package, which may belong to a
content management system or forum, the intruders easily produce
a copy of a piece of your code. It may be also a security risk if it
is a poorly designed one.
</simpara>
<simpara>
These attacks are mainly based on exploiting the code not being written
with security in mind. Never trust on any kind of input, especially
which comes from the client side, even though it comes from a select box,
a hidden input field or a cookie. The first example shows that such a
blameless query can cause disasters.
</simpara>
<itemizedlist>
<listitem>
<simpara>
Never connect to the database as a superuser or as the database owner.
Use always customized users with very limited privileges.
</simpara>
</listitem>
<listitem>
<simpara>
Check if the given input has the expected data type. PHP has
a wide range of input validating functions, from the simplest ones
found in <link linkend="ref.variables">Variable Functions</link> and
in <link linkend="ref.ctype">Character Type Functions</link>
(e.g. <function>is_numeric</function>, <function>ctype_digit</function>
respectively) onwards the
<link linkend="ref.pcre">Perl compatible Regular Expressions</link>
support.
</simpara>
</listitem>
<listitem>
<para>
If the application waits for numerical input, consider to verify data
with <function>is_numeric</function>, or silently change its type
using <function>settype</function>, or use its numeric representation
by <function>sprintf</function>.
<example>
<title>A more secure way to compose a query for paging</title>
<programlisting role="php">
<![CDATA[
settype($order, 'integer');
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
// please note %d in the format string, using %s would be meaningless
$query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;", $offset);
]]>
</programlisting>
</example>
</para>
</listitem>
<listitem>
<simpara>
Quote each non numeric user input which is passed to the database with
<function>addslashes</function> or <function>addcslashes</function>.
See <link linkend="security.database.storage">the first example</link>.
As the examples shows, quotes burnt into the static part of the query
is not enough, and can be easily hacked.
</simpara>
</listitem>
<listitem>
<simpara>
Do not print out any database specific information, especially
about the schema, by fair means or foul. See also <link
linkend="security.errors">Error Reporting</link> and <link
linkend="ref.errorfunc">Error Handling and Logging Functions</link>.
</simpara>
</listitem>
<listitem>
<simpara>
You may use stored procedures and previously defined cursors to abstract
data access so that users do not directly access tables or views, but
this solution has another impacts.
</simpara>
</listitem>
</itemizedlist>
<simpara>
Besides these, you benefit from logging queries either within your script
or by the database itself, if it supports. Obviously, the logging is unable
to prevent any harmful attempt, but it can be helpful to trace back which
application has been circumvented. The log is not useful by itself, but
through the information it contains. The more detail is generally better.
</simpara>
</sect3>
</sect2>
</sect1>
<sect1 id="security.errors">
<title>エラーのレポート</title>
<para>
PHPのセキュリティに関して、2種類のエラー出力があります。一つは、セ
キュリティ向上に役立つものであり、もう一つは、セキュリティ上有害な
ものです。
</para>
<para>
標準的な攻撃手法の中に不完全なデータをシステムに送信し、返されるエ
ラーの種類と内容を調べることにより、システムを調べるというものがあ
ります。これにより、システムのクラッカーがありうる弱点を調査するた
めにそのサーバに関する情報を調べることが可能になります。
例えば、ある攻撃者が事前のフォーム投稿の際にあるページに関して収集
した情報を持っている場合、変数を上書きしたり、修正したりしようとす
るかもしれません。
<example>
<title>カスタムHTMLページにより変数を攻撃する</title>
<programlisting role="php">
<![CDATA[
<form method="post" action="attacktarget?username=badfoo&password=badfoo">
<input type="hidden" name="username" value="badfoo">
<input type="hidden" name="password" value="badfoo">
</form>
]]>
</programlisting>
</example>
</para>
<para>
通常返されるPHPのエラーは、エラーを生じた関数やファイル、エラーを
発生したPHPファイル、エラーを発生した行番号のような情報が含まれて
おり、スクリプトをデバッグする開発者に非常に有用です。これらが調べ
ることができる情報の全てです。デバッグ目的でPHPの開発者が
<function>show_source</function>,
<function>highlight_string</function>,
<function>highlight_file</function> を使用することはまれではありま
せん。しかし、実用サイトでは、これは秘密の変数、未確認の構文、その
他の危険な情報を公開することになってしまいます。特に危険なのは、
組み込みのデバッグハンドラを有する既知のソースからのコードを実行して
いるか、一般的なデバッグ技法を使用している場合です。攻撃者が、使用し
ている一般的な技法を特定できた場合、次のようにあるページに様々な一般
的なデバッグ用文字列を送信することによりしらみつぶしに攻撃しようとす
るかもしれません。
<example>
<title>一般的なデバッグ変数を探す</title>
<programlisting role="php">
<![CDATA[
<form method="post" action="attacktarget?errors=Y&showerrors=1"&debug=1">
<input type="hidden" name="errors" value="Y">
<input type="hidden" name="showerrors" value="1">
<input type="hidden" name="debug" value="1">
</form>
]]>
</programlisting>
</example>
</para>
<para>
エラー処理の方法の方法のいかんによらず、エラーを調べる機能は、攻撃
者により多くの情報を与えることにつながります。
</para>
<para>
例えば、多くの一般的なエラー形式では、システムはPHPを実行している
ことを示します。攻撃者が .html ページを調べ、(システムの既知の弱点
を探すために)誤ったデータを送信することによりバックエンドを調べた
いと思った場合、システムをPHPと共に構築されていることを知ることが
可能となる可能性があります。
</para>
<para>
関数エラーは、システムが特定のデータベースエンジンが実行しているこ
と、または、Webページのプログラム内容や設計に関する鍵を示すことが
あります。これにより、データベースポートをオープンしたり、Webペー
ジに固有のバグや弱点を調べるといったより詳細な調査を行うことが可能
となります。例えば、異なった不正なデータを送信することにより、攻撃
者は、(エラー行番号から)そのスクリプトの異なる場所を調べることと同
時にそのスクリプトの認証の順番を定義することが可能です。
</para>
<para>
ファイルシステムまたは一般的なPHPエラーは、Webサーバが有する許可属
性やWebサーバのファイル構造を示すことがあります。エラーコードを書
く開発者は、元は秘密の情報を容易に公開することにより、この問題を悪
化させる可能性があります。
</para>
<para>
この問題に対しては3種類の対策があります。最初の対策は、全ての関数
をよく調べ、大部分のエラーの修正を試みることです。2番目は、実行す
るコードのエラーリポートを完全に無効にすることです。3番目は、
PHPのカスタムエラー処理関数を使用して独自のエラーハンドラを作成す
ることです。システムのセキュリティポリシーに基づき、これらの3種類
の対策が適用可能かどうかを判定します。
</para>
<para>
この問題を事前に防止する手段の一つは、PHPの
<function>error_reporting</function>を使用することです。これにより、
コードを安全にするための手がかりが得られ、危険と思われる変数が使用
されている部分をみつけることが可能です。実使用の前にE_ALLを指定して
コードをテストすることにより、変数が他の手段で汚染されたり、修正さ
れたりする可能性がある部分を簡単に見つけることが可能です。
実使用の準備ができた際には、E_NONEを使用することにより、コードを外
部から調べられることを防止することが可能です。
<example>
<title>E_ALLで危険な変数を見つける</title>
<programlisting role="php">
<![CDATA[
<?php
if ($username) { // 使用前に初期化または確認されていない変数
$good_login = 1;
}
if ($good_login == 1) { // 上のテストが失敗した場合、使用前に初期化または確認されていない
fpassthru ("/highly/sensitive/data/index.html");
}
?>
]]>
</programlisting>
</example>
</para>
</sect1>
<sect1 id="security.registerglobals">
<title>グローバル変数への登録を使用</title>
<para>
セキュリティを向上させるために使用可能なPHPの機能の一つは、
PHPでregister_globals = offを指定して設定を行なうことです。
ユーザが投稿した変数が全てPHPコードに導入されるこの機能を
オフにすることにより、潜在的な攻撃者が攻撃できる変数の数を
減らすことが可能となります。これにより、投稿を作成するため
により多くの時間を要し、内部変数はユーザにより投稿されたデ
ータから効率的に隔離することが可能となります。
</para>
<para>
この場合、PHPで動作させるために必要なコードの量は若干増加
しますが、その利益は努力を上回ると議論されています。
<example>
<title>register_globals=offを指定しない場合の動作</title>
<programlisting role="php">
<![CDATA[
<?php
if ($username) { // get/post/cookiesで上書きされる可能性がある
$good_login = 1;
}
if ($good_login == 1) { // get/post/cookiesで上書きされる可能性がある
fpassthru ("/highly/sensitive/data/index.html");
}
?>
]]>
</programlisting>
</example>
<example>
<title>register_globals = offを指定した場合の動作</title>
<programlisting role="php">
<![CDATA[
<?php
if($HTTP_COOKIE_VARS['username']){
// クッキーから来るもののみ
$good_login = 1;
fpassthru ("/highly/sensitive/data/index.html");
}
?>
]]>
</programlisting>
</example>
これを賢く使用することにより、偽の入力が試みされた際に警告を
発生する防止策をとることさえ可能になります。変数が入力される
場所を事前に正しく知っている場合、投稿されたデータが適当な場
所からのものであるかを調べることが可能です。これは、データが
偽造されていないことを保証するものではありませんが、攻撃者に
正しい偽造の方法を推定することを強いることになります。
<example>
<title>簡単な変数の汚染の有無の検出</title>
<programlisting role="php">
<![CDATA[
<?php
if ($HTTP_COOKIE_VARS['username'] &&
!$HTTP_POST_VARS['username'] &&
!$HTTP_GET_VARS['username'] ) {
// ユーザ名を認証する他の手段を実行する
$good_login = 1;
fpassthru ("/highly/sensitive/data/index.html");
} else {
mail("admin@example.com", "侵入が試みられました", $HTTP_SERVER_VARS['REMOTE_ADDR']);
echo "管理者による警告:セキュリティ違反です。";
exit;
}
?>
]]>
</programlisting>
</example>
もちろん、register_globalsをoffに変えただけでは、コードが安全である
ことを意味しません。投稿されたデータのあらゆる部分について、他の
手段でも確認する必要がありません。
</para>
</sect1>
<sect1 id="security.variables">
<title>ユーザが送信したデータ</title>
<para>
多くのPHPで最も脆弱な部分は、言語自体に起因するものではなく、単に
セキュリティを考慮して書かれていないコードの問題です。この原因につ
いて、指定したコードの部分の意味を常に時間をかけて吟味し、予想外の
変数が投稿された場合に有り得る損害を確かめる必要があります。
<example>
<title>危険な変数の使用</title>
<programlisting role="php">
<![CDATA[
<?php
// ユーザのホームディレクトリからファイルを削除します... または他の誰
// かのディレクトリかも?
unlink ($evil_var);
// 彼らのアクセスのログを書き込む.. または違うかも?
fputs ($fp, $evil_var);
// 何かちょっとしたことを実行.. または rm -rf *?
system ($evil_var);
exec ($evil_var);
?>
]]>
</programlisting>
</example>
常に注意してコードをテストし、Webブラウザから投稿された全ての変数
について次のような点を確認して下さい。
<itemizedlist>
<listitem>
<simpara>
このスクリプトは、意図したファイルのみを受け付けるか?
</simpara>
</listitem>
<listitem>
<simpara>
例外的なまたは意図したもの以外のデータにより実行することが可能
か?
</simpara>
</listitem>
<listitem>
<simpara>
このスクリプトは意図した以外の方法で使用することが可能か?
</simpara>
</listitem>
<listitem>
<simpara>
このスクリプトは、悪い意味で他のスクリプトと組み合わせて使用す
ることが可能か?
</simpara>
</listitem>
<listitem>
<simpara>
トランザクションは適切に記録されているか?
</simpara>
</listitem>
</itemizedlist>
スクリプトを書いた後ではなく、書いている時にこれらの質問を適宜行う
ことにより、セキュリティ改善のために不幸にして書き直しが必要になる
ということを避けることができます。こうした考慮をまず行うことにより、
システムのセキュリティを保証できるわけではありませんが、改善の一助
にはなりえます。
</para>
<para>
register_globals,magic_quotes, または他の便利な設定は、有効性、発
信元、指定した変数の値について混乱を生じる可能性があるため、設定を
オフにしたいと思うかもしれません。error_reporting(E_ALL) モードで
PHPを動作させた場合、確認または初期化する前に使用された変数に関し
て警告を発生させることも可能です。(これにより、処理時に通常とは異
なるデータを防止することが可能です)
</para>
</sect1>
<sect1 id="security.hiding">
<title>PHPの隠蔽</title>
<para>
一般に隠蔽という手段はセキュリティとしては弱いものだと言われています。
しかしこうした手法が望ましい場合もあります。
</para>
<para>
PHPを隠すための簡単な技法がいくつかあり、システムの弱点を見つけよ
うとする攻撃を遅延させることができる可能性があります。php.iniファ
イルでexpose_php = offと設定することにより、攻撃者が利用可能な情
報を減らすことが可能です。
</para>
<para>
他の手段は、ApacheのようなWebサーバをPHPに異なるファイル形式をパー
スさせるように設定することです。これは、.htaccessディレクティブま
たはApacheの設定ファイル自体で指定します。これにより、紛らわしい
ファイル拡張子を使用可能です。
<example>
<title>PHPを他の言語として隠す</title>
<programlisting role="apache-conf">
<![CDATA[
# PHPコードを他のコード型のようにする
AddType application/x-httpd-php .asp .py .pl
]]>
</programlisting>
</example>
または、次のように完全に隠すことも可能です。
<example>
<title>PHP拡張子用に未知の型を使用する</title>
<programlisting role="apache-conf">
<![CDATA[
# Make PHP code look like unknown types
AddType application/x-httpd-php .bop .foo .133t
]]>
</programlisting>
</example>
または、HTMLコードとして隠すことも可能です。この場合、全てのHTMLファ
イルがPHPエンジンを通じてパースされることになるため、若干の性能上の
問題があります。
<example>
<title>PHP拡張子としてHTML型を使用する</title>
<programlisting role="apache-conf">
<![CDATA[
# 全てのPHPコードをHTMLのように作成する
AddType application/x-httpd-php .htm .html
]]>
</programlisting>
</example>
効率的にこれを使用するには、全てのPHPファイルの名前を上の拡張子に変更
する必要があります。これは、あいまいさに基づく形式のセキュリティですが、
欠点の少ない簡単な防衛策です。
</para>
</sect1>
<sect1 id="security.current">
<title>最新版を維持する</title>
<simpara>
PHPは、他の大規模なシステムと同様に、常に調査、改良されています。
各新バージョンにはしばしばセキュリティ上の問題や設定上の問題、そし
て、システム全体のセキュリティや安定性に影響するその他の問題を修正
するためのメジャーまたはマイナーな変更が含まれています。
</simpara>
<simpara>
他のシステムレベルのスクリプト言語やプログラムと同様に、最善のアプ
ローチは、頻繁に更新し、最新のバージョンとその変更を注視し続けるこ
とです。
</simpara>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"../../manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
-->
|