ラベル http の投稿を表示しています。 すべての投稿を表示
ラベル http の投稿を表示しています。 すべての投稿を表示

2013年12月14日土曜日

IEのバージョン別の X-Content-Type-Options: nosniffの対応状況

IE8以下は内容がHTMLっぽいとHTTPレスポンスヘッダ‐のContent-typeに関わらず、HTMLとしてレンダリングしてしまう。
IE8はnosniffをつければ、Content-typeに従う。

参考: X-Content-Type-Options: nosniffの効果を確認してみる | UCWD-Studio.【ホームページ制作 / 京都】


PHPの実験用コード

<?php header('Content-type: text/plain') ?>
<script>alert(1)</script>


IE9以降およびChrome・Firefoxなどは、nosniffがある場合はJavaScriptやCSSの外部ファイルのContent-typeが妥当でないと、その外部ファイルは読み込まない。

参考:
X-Content-Type-Options: nosniff の効果 : swdyh
MIME タイプのセキュリティ リスクの軽減 (Windows)

2013年12月10日火曜日

今どきのHTTPヘッダーによるセキュリティ向上の調査メモ

今夜つける HTTPレスポンスヘッダー (セキュリティ編) - うさぎ文学日記の中で、よく知らなかったヘッダーについて調べたメモ。

いくつかのヘッダーについてのブラウザ別のサポート状況はこちら。
(ちょっと古いが、少なくともこれ以降のバージョンではサポートしているということは分かる。)

IPA ISEC セキュア・プログラミング講座:Webアプリケーション編 第8章 マッシュアップ:クライアントサイドマッシュアップ: #4 対策に利用できる技術


X-XSS-Protection

IE8以降のXSSフィルターはデフォルトで有効になっている(ブロッキングモードではない)。

Security through HTTP response headers

[IEInternals] XSS フィルターを制御する | Hebikuzure's Tech Memo

Internet Explorer 8 には、XSS フィルターとして知られている、反射型クロスサイト スクリプティング攻撃を防止するのに有効な画期的新機能があります。このフィルターは既定ではインターネット ゾーン、信頼済みサイト ゾーン、制限付きサイト ゾーンで動作します。

オフにしている人や、XSS on XSS Filterに備えて、ブロッキングモードで有効にさせるのが無難?
X-XSS-Protection: 1; mode=block


Content-Security-Policy

(参考)Content Security PolicyでXSSを断ち切る | monoの開発ブログ

インラインリソースは'unsafe-inline'で許可できる。
dataスキームURIには"data:"で許可できる。

An Introduction to Content Security Policy - HTML5 Rocks

IEがほぼサポートしていないようなので、効果は限定的か。

CSP policy directives - Security | MDN
CSP is quite usable in Chrome 16+, Safari 6+, and Firefox 4+, and has (very) limited support in IE 10.

セキュリティとしては非常に強力だが、ホワイトリストのメンテが面倒なので外部リソースを気軽に使うようなサイトでは使うのは難しい?

あるいは、こんな風に思いっきり緩くしておくか?(これだと意味がないか...)
Content-Security-Policy:
default-src 'self';
script-src 'unsafe-inline' *;
img-src data: *;
frame-src *;
font-src *;
style-src 'unsafe-inline' *;
report-uri /csp-report.php

見やすくするために改行している。未検証なので取扱注意。
faviconはimg-srcに含まれる。
違反が見つかったら、report-uriにJSONでPOSTされる。
eval()が必要なら'unsafe-inline'も追加する。

Content-Security-Policy-Report-Onlyでチェックのみに使うのが良いかもしれない。


Strict-Transport-Security

IEが10でもサポートしていないことを考えると、これが効かない場合のリダイレクト等の処理も必要。

HTTP Strict Transport Security - OWASP

2011年4月1日金曜日

PHPで 簡単にGET / POSTできるように file_get_contents()を拡張する

file_get_contents()は便利だが、POSTとかエラー対応とかは少し面倒なので、手軽に使えるようにラップしてみた。
PHPのバージョンは5.2.10以降または5.3.0以降が対象。

function file_http(
    $url,
    $params = null,    //パラメータがあれば配列で渡す
    $method = 'GET',    //GET or POST
    &$status = null,    //参照渡しでレスポンスのHTTP Status Codeを返す
    &$http_response_header = null,    //参照渡しでレスポンスのHTTP Headerの配列を返す
    $request_header = array()    //追加したいリクエストのHTTP Headerがあれば
) {
  
    $opt = array(
        'method' => $method,
        'header' => (array)$request_header,    //PHP 5.2.10 / 5.3.0 以降は配列でもOK
        'ignore_errors' => true,    //PHP 5.2.10 / 5.3.0 以降で有効
    );
  
    if ($params) {
        $query_string = http_build_query($params);
      
        if ($method === 'GET') {
            $url .= '?' . $query_string;
        } else {    //POSTの場合
            $opt['header'][] = 'Content-type: application/x-www-form-urlencoded';
            $opt['content'] = $query_string;
        }
    }
  
    $res = file_get_contents($url, false, stream_context_create(array('http' => $opt)));
  
    //レスポンスのHTTP Headerは$http_response_headerにセットされている
    if (is_array($http_response_header)) {
        preg_match('@^HTTP/1\\.. ([0-9]{3}) @i', $http_response_header[0], $matches);
        $status = $matches[1];
    }
  
    return $res;
}

使用例
$res = file_http(
    'http://localhost/test.php',
    array('foo' => 'テスト'),
    'POST',
    $status,
    $responseHeaders,
    array('User-Agent: ゆーざーえーじぇんと', 'Referer: りふぁら')    // 1つだけの場合は文字列でもOK
);

echo $status;    // => "200"
var_dump($responseHeaders);

実際に使う場合には結果の判断等でレスポンスのHTTPステータスコードが欲しい場合があるので、パラメータの参照渡しで取得できるようにした。
そしてステータスコード以外のHTTP Headerを参照したいケースもあるので、これも参照渡しで配列として取得できるようにした。

また、ignore_errorsを指定することにより、レスポンスのHTTPステータスコードが400以上でもWarningを出さない、かつレスポンスのBodyを取得できるようにした。(参考:[メモ] PHPのfile_get_contentsを、HTTPリクエストに使うときのTIPS ::ハブろぐ
ただしホスト名の名前解決ができない場合はその手前でWarningが出てしまうので、PHPで URLのホスト名(ドメイン)が存在するかチェックする方法のような対応が必要。


以下は余談。
普段は変数名等はcamelCaseにするのだが、$http_response_header等に引っ張られてアンダーバー繋ぎにしてみた。
関数名は、
  • http_request
  • http_get
  • httpRequest
などを考えたのだが、どれもPECLで既に使われているので諦めた。

2010年12月27日月曜日

PHPで CSVをダウンロードするための参考サイトとサンプルコード

CSVをダウンロードするWebアプリをPHPで実装する方法。
しばしば必要になるが、そのたびに調べ直してるので、後日のためにまとめてみる。



まとめるとこんな感じか。
$fileName = 'ダウンロードして保存する時のファイル名.csv';
$fileName =  mb_convert_encoding($fileName, 'SJIS-WIN');

header('Content-Type: application/x-csv');
header("Content-Disposition: attachment; filename=$fileName");

$fp = fopen('php://output', 'w');

$rows = /* 配列の集合(DBから取得したデータとか) */

foreach ($rows as $row) {
    mb_convert_variables('SJIS-WIN', mb_internal_encoding(), $row);
    fputcsv($fp, $row);
}

fclose($fp);

2010年11月21日日曜日

PHPで 「Webページの有効期限が切れてます」となる時の傾向と対策

PHPでフォーム等を作った場合、Webブラウザの戻るボタンやJavaScriptのhistory.back()で前のページに戻った時に「Webページの有効期限が切れてます」と表示されることがある。

上記はIEの場合で、ブラウザによって少し挙動が違う(下記)。
いずれもページを更新(リロード)するとサーバにPOSTが再送信され、ページが表示される。
  • IE
    • 上記(IE8の例)のような画面が表示される。
  • Firefox
    • 「このページを表示するにはフォームデータを再度送信する必要があります。フォームデータを再送信すると以前実行した検索、投稿や注文などの処理が繰り返されます。」という確認ダイアログが表示され、「再送信」ボタンと「キャンセル」ボタンが表示される。「再送信ボタン」をクリックするとページが表示される。
  • Chrome
    • 「フォーム再送信の確認
      このウェブページを正しく表示するには、先ほど入力したデータが必要です。データをもう一度送信することは可能ですが、このページで行った操作をすべて繰り返すことになります。データを再送信してこのページを表示する場合は [再読み込み] をクリックしてください。」というメッセージのみの画面が表示される。
  • Safari
    • このようなダイアログが表示される。(Windows版で確認。Mac版は分からない。)



犯人はだれだ?


原因は、PHPでSESSIONを使うと(デフォルト設定では)自動でキャッシュ制御用のHTTPヘッダーが送出され、それによりクライアント側のキャッシュが使用不可にされるため。(参考:floatingdays: PHPでブラウザキャッシュを有効にする

つまり、下記条件を全て満たした場合にこの現象が発生する。
  1. HTTP POSTで遷移してきた。
  2. SESSIONを使っている。(session_start()してるか、php.ini等でsession.auto_start=1に設定している。)
  3. 次のページに行ってから、ブラウザの履歴機能(JavaScriptのhistory.back()を含む)で戻ってきた。



じゃあどうすればいいの?

対策として有名なのは、session_start()する前に、session_cache_limiter('none')とすること。
session_cache_limiter('none');
session_start();
これにより、SESSIONを使っても余計なHTTPヘッダーが送出されなくなる。

または、php.ini等でsession.cache_limiterに"none"を設定しても同じことになる。(おそらく元ネタはこのあたりだろう → PHP: session_cache_limiter - Manual

実際にこれで問題は解決する。



異論反論オブジェクション! [shut very bad!]

しかし、「PHP/「ページの有効期限切れ」対策 - Glamenv-Septzen.net」によると、これはPHPが想定しているパラメータではなく、お行儀のよいやり方ではないらしい。

'none'というパラメータは正しいパラメータではなく、それゆえに何もHTTPヘッダーを送出しないという挙動になるらしい。
(ただし、もし「規定外のパラメータはスルーする」というのが意図した仕様だとしたら、'none'でも何でも「正しいパラメータ」だけど。)

上記の記事ではsession_cache_limiter('none')ではなくsession_cache_limiter('private_no_expire')を推奨している。
「ページの有効期限切れ」をsession_cache_limiter()で解決 - shinyanakaoのよすがブログ」でも同様にprivate_no_expireを推奨している。

しかし、実際にsession_cache_limiter('private_no_expire')を使うと、やはり余分なHTTPヘッダーが送出される。

private_no_expireの場合、(privateに比べて)Expiresが送出されなくなるが、「Cache-Control: max-age=(session.cache_expire ぶんだけ未来)」が送出されるため、やはりブラウザに影響が出てしまう。(参考:現在のキャッシュリミッタを取得または設定する - PHP 5.3 日本語マニュアル
Firefoxでは問題ないが、IEだとリロード時にもキャッシュを使ってしまい、サーバからのリロードができなくなるようだ。(参考:Webアプリケーション開発ラボ by NPO情報活用センター - PHP:キャッシュ問題について。PHP Tips|ワークスポット・ジェーピー
おそらくsession.cache_expireで設定されている期間はキャッシュが有効になるのだろう。(Expiresでそう指定しているのだから、IEは悪くない。)

なので、お行儀が悪くてもsession_cache_limiter('none')を使うしかないのでは?('none'じゃなくて'hoge'でも'hage'でも「正しい」パラメータ以外なら何でもいいけど。)



新たな選択肢

しかし、こういう手もあるよ。
session_start();
header('Expires:'); //下記「余談」の追記も参照
header('Cache-Control:');
header('Pragma:');

header()でセミコロンの右に何も書かないと、PHPは何も送出しないようだ。
これにより、session_start()のHTTPヘッダー送出を上書きし、結果的に何も送出しない。

session_cache_limiter('none')より冗長だが、明示的という意味ではいいかもしれないと思っている。
(キャッシュを有効にしたい場合にも応用が効く。)



余談

上記のようにブラウザキャッシュの無効化を無効化すると、当然ながらSESSIONの最新情報が反映されていないブラウザキャッシュをブラウザが表示してしまうので注意。

(2011/1/7 追記)
IEはキャッシュがあり、かつ、そのキャッシュがExpiresを何も指定されていないと、アドレスバーにURLを直接入力された場合やリダイレクトした場合などにサーバにアクセスせずにキャッシュの方を使ってしまう。(Firefoxはその場合もサーバにアクセスしてくれる。)
ブラウザの履歴機能を使うためにキャッシュはさせたいが、上記の場合にはサーバにアクセスさせたい場合は、Expiresで-1を指定すると良いようだ。
header('Expires: -1');
ログイン管理をする場合などはこれをやっておいた方が良さそう。
(追記終わり)

また、POSTのパラメータに「チケット」(=ワンタイムトークン)を入れることによる二重POST防止を推奨するのは正しい。というか二重POSTを確実に防ぎたいと思ったらこれしかない。


2010年10月26日火曜日

ファイル1つだけで出来ている 便利なPHP製ツールいろいろ

PHPライブラリは新時代に入った。定番だったphpMyAdminやPEAR::Mailはもう古い。最近は1ファイル配置するだけで簡単に使える便利なツールが増えてきた。

例えばこんなの。



次は1ファイルのフレームワークだね。(Google Code上にはもうたくさん公開されてるけど...)
あるいは1ファイルのブログシステムやWikiか?

2010年5月18日火曜日

PHPの header()でリダイレクト以外でも HTTPステータスコードを指定する

PHPのheader()を使ってリダイレクトをさせる場合、デフォルトではHTTPステータスコードは302(Found、一時的なURL変更等)になる。
リダイレクト時のHTTPステータスコード301(Moved Permanently、恒久的なURL変更)にしたい場合、header()の第3引数に301を指定する。

header('Location: http://www.example.com/', true, 301);
参考:生の HTTP ヘッダを送信する - PHP 5.3 日本語マニュアル



この第3引数でのHTTPステータスコードの指定だが、リダイレクト以外でも使えるか試してみた。
<?php header('X-Test: TEST', true, 404) ?>

テストです。

結果(HTTPレスポンスヘッダーから一部を抜粋)
HTTP/1.1 404 Not Found
X-Test: TEST

できた。

これを使えば、header()でHTTPステータスコードを指定し、なおかつ他のHTTPレスポンスヘッダーの値をセットしたい場合に、1行で処理できる。
あまりそんな機会はないか。

2010年5月8日土曜日

Webでの静的ファイル取得について Google App Engineと Google Codeを比較

(2010/5/11 修正:Google Codeでもmax-ageが設定可能だったので一部修正。参考:SubversionFAQ - support - Subversion FAQ - Project Hosting on Google Code


スタティックなファイルのhttp(s)での取得について、Google App EngineとGoogle Codeを比較してみる。


Google App Engine
Google Code
https


gzip
あり
なし
Etag
あり
あり
Last-Modified
なし
あり
max-age
(Expires)
設定可能
デフォルトは10分
設定可能
デフォルトは1分

Server
Google Frontend
Apache


URLは下記のような感じ。App Engineの方がちょっと短い。
Google App Engine → http://<アプリ名>.appspot.com/<パス>
Google Code → http://<プロジェクト名>.googlecode.com/svn/<パス>


Google App Engineのメリット
gzipで圧縮されるのでトラフィックを減らすことができる。
Google Codeの方はmax-ageはまだいいとしても、Google Codeはgzipされないのが痛い。
主なクライアントがSubversion(等)のクライアントソフトだから仕方ないか。


Google Codeのメリット
そのままSubversion(等)でバージョン管理できる。使い慣れたクライアントソフトで制御できるので使い易い。
Google App Engine Launcherは毎回パスワードを訊かれるのが面倒。


Google App Engineは転送量等に制限があるが、かといってGoogle Codeでもそんなにも使ったらBanされるんだろう。
(参考:floatingdays: Google Codeの JavaScriptファイルを外部サイトから読み込んでも良い?

それから、Google Codeではその性格上オープンソースとして公開する必要がある。

2009年5月7日木曜日

PEAR::HTTP_Client 非徹底解剖

PEAR::HTTP_Clinetがget()する時の、主にコネクションを確立するまでの大まかなfunctionの流れ。


HTTP_Client#get()

HTTP_Client#_performRequest()

HTTP_Request#sendRequest() ※1
↓ ↓ ※2
↓ Net_Socket#connect() ※3
↓ ↓ ↓
↓ ↓fsockopen()
↓ ↓ ※4
↓ socket_set_blocking() ※5

HTTP_Response#process() ※6

ソケット経由で得たレスポンスを処理していく
(以下省略)


※1 将来的にはHTTP_Request2に切り替える可能性が高いと思われる
※2 URLのスキームがhttpsの場合は、ホスト名は「ssl://ホスト名」に、そうでない場合はIPアドレスに変換される
※3 ちなみにHTTP_Request2の場合、自前のSocketクラス(HTTP_Request2_Adapter_Socket)を使っている
※4 もしこのあたりでNet_Socket#enableCrypto()を使えばSSLのバージョンを指定できるけど、使っていない
※5 stream_set_blocking()のエイリアス。この流れの中ではデフォルト設定のままなので、常にブロッキングを有効にしている
※6 HTTP_Responseクラスは、HTTP_Requestのファイル(Request.php)内で定義されている

サーバサイドでHTTP POSTする PHPライブラリいろいろ

cURL

PEAR::HTTP_Request
(もうメンテナンスモードになったらしい)
This package has been superseded, but is still maintained for bugs and security fixes. Use HTTP_Request2 instead.

PEAR::HTTP_Request2 (まだα版(0.4.0))

PEAR::HTTP_Client (PEAR::HTTP_Requestをラップしていて使いやすい。現時点ではこれがベストか)

Snoopy (htmlsqlでも内部的に使われている、わりと有名なライブラリ)

Zend_Http_Client


CakePHP HttpSocket

P2_Services_Base

2009年1月26日月曜日

PHPの file_get_contents()が成功する HTTPステータスコード

PHPのfile_get_contents()でhttp経由でデータを取得する場合、HTTPステータスコードによっては取得が失敗になる。(Warningが出て、戻り値がfalseになる。)
取得が失敗しないHTTPステータスは以下の通り。(数字の部分で判断するので、後ろの文字列は何でもよいが参考までに載せておく。)

  • 200 OK
  • 206 Partial Content
  • 301 Moved Permanently
  • 302 Moved Temporarily
  • 303 See Other

206が意外。

ブログ アーカイブ

tags