ホームページ  >  記事  >  バックエンド開発  >  PHP の高度な 10 のヒント [改訂版]_PHP チュートリアル

PHP の高度な 10 のヒント [改訂版]_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-21 15:25:59922ブラウズ

1. SQL インジェクションのチートシートを使用する
基本原則は、ユーザーが送信したデータを決して信頼しないことです。
もう 1 つのルールは、データを送信または保存するときにデータをエスケープすることです。
これは、入力フィルター、出力エスケープ (FIEO) として要約できます。 SQL インジェクションの脆弱性の通常の原因は、次のステートメントにあるように、入力がフィルターされていないことです:

コードをコピー コードは次のとおりです:
$query = "SELECT *
ユーザーから
WHERE name = '{$_GET['name']}'";

この例では、$_GET['name'] はユーザーによって送信されたデータから取得されており、エスケープもフィルターもされていません~ ~
出力をエスケープする場合、プログラム外で使用することを目的としたデータはエスケープする必要があることを覚えておく必要があります。エスケープしないと、正しく解析されない可能性があります。
対照的に、入力をフィルタリングすると、使用前にデータが正しいことが保証されます。
フィルタリングされた入力では、プログラム外の生データは信頼できないため、フィルタリングする必要があることに注意してください。
次の例は、入力フィルタリングと出力エスケープを示しています:

コードをコピー コードは次のとおりです:
// フィルタリングされたデータとエスケープされたデータの配列をそれぞれ初期化します
$clean = array。 () ;
$sql = array();
// 名前をフィルターします (簡単にするために、アルファベットの名前が必要です。)
if (ctype_alpha($_GET['name']) {
$clean['name' ] = $_GET['name'];
} else {
// ここで何かを行ってください
// 名前をエスケープします。
$sql['name'] = mysql_real_escape_string(' name' ]);
$query = "SELECT *
FROM users
?> SQL インジェクションを防ぐ方法は、次のような準備ステートメントを使用することです。



コードをコピーします。 コードは次のとおりです。

// クエリ形式を指定します。
$query = $db- >prepare('SELECT * FROM users WHERE name = :name'); // クエリ データを提供し、クエリを実行します $query->execute(array('name' => $clean[ 'name'])); ?>



2. 比較演算子の違いを理解する

たとえば、文字列内に部分文字列が存在するかどうかを検出するには、strpos() を使用します (部分文字列が見つからない場合は、関数は FALSE を返します)、結果はエラーにつながる可能性があります:



コードをコピー

コードは次のとおりです:
$authors = 'Chris & Sean'; $authors, 'Chris')) {
echo 'Chris is an author.'; } else { echo 'Chris is not an author.' }
部分文字列は先頭にあります。strpos() この関数は正しく 0 を返し、部分文字列が文字列の先頭にあることを示します。次に、条件文は結果をブール型 (Boolean) として扱うため、PHP では 0 が FALSE として計算され、最終的に条件文が失敗します。
もちろん、このバグは厳密な比較ステートメントで修正できます:



コードをコピー

コードは次のとおりです:

if (strpos($authors, 'Chris') !== FALSE) {
echo 'Chris is an author.'; else {
echo 'Chris is not an author.';
?> 3. else をショートカットします。 「変数を使用する前に必ず初期化してください」に注意してください。
ユーザー名に基づいてユーザーが管理者であるかどうかを検出するには、次の条件ステートメントを検討します。



コードをコピーします。

コードは次のとおりです。


if (auth($username) = = 'admin ') {
$admin = TRUE; } else {
$admin = FALSE;
?>

これは一目で理解できるので十分安全だと思われます。便宜上、名前と電子メールの両方の変数を設定する、より複雑な例を想像してください: コードをコピーします
コードは次のとおりです:

if (auth($username) == 'admin') {
$name = 'admin@example.org';
$admin = TRUE; {
/* データベースから名前とメールアドレスを取得します。 */
$query = $db->prepare('SELECT name, email
FROM users
WHERE username = :username'); (array('username' => $clean['username']));
$result = $query->fetch(PDO::FETCH_ASSOC);
$name = $result['name']; email = $result['email'];
$admin = FALSE;
?>


$admin は明示的に TRUE または FALSE に設定されているため、すべて問題ないようです。ただし、後で別の開発者が elseif ステートメントをコードに追加した場合、その開発者はそのことを忘れる可能性があります:


コードをコピーします

コードは次のとおりです:
if (auth ($username ) == 'admin') { $name = 'admin@example.org';
$admin = TRUE; ') {
$name = 'Moderator';
$email = 'mod@example.org';
$moderator = TRUE;
/* 名前とメールアドレスを取得します。 -> prepare('ユーザー名、メールアドレスを選択
WHERE ユーザー名 = :ユーザー名');
$query->execute(array('ユーザー名' => $clean['ユーザー名']));結果 = $クエリ->fetch(PDO::FETCH_ASSOC);
$name = $result['name'];
$admin = FALSE; ;
}
?>


elseif 条件をトリガーするユーザー名をユーザーが指定すると、$admin が初期化されず、望ましくない動作、またはさらに悪いことにセキュリティの脆弱性が発生する可能性があります。さらに、最初の条件で初期化されていない $moderator 変数にも同様の状況が存在します。
$admin と $moderator を初期化することで、この状況を回避するのは非常に簡単です:



コードをコピーします

コードは次のとおりです:


$admin = FALSE;
$moderator = FALSE; ;
if (auth($username) == '管理者') {
$name = 'admin@example.org';
$admin = TRUE; ) == 'mod') {
$name = 'Moderator'; $moderator = TRUE; /* 名前とメールアドレスを取得します。 $query = $db->prepare('SELECT name, email FROM users WHERE username = :username');
$query->execute(array('username' => $clean['username' ] ));
$result = $query->fetch(PDO::FETCH_ASSOC);
$name = $result['email'];


コードの残りの部分が何であれ、明示的に別の値に設定されない限り $admin が FALSE であることは明らかです。 $moderator についても同様です。起こり得る最悪の事態は、$admin または $moderator がいかなる条件下でも変更されず、その結果、管理者またはモデレーターである人が、対応する管理者またはモデレーターとして扱われないことです。
何かをショートカットしたいのに、例に else が含まれていることを見て少しがっかりした場合。興味があるかもしれないボーナスのヒントがあります。これが近道と言えるかどうかはわかりませんが、それでも役立つことを願っています。
ユーザーが特定のページを表示する権限を持っているかどうかを検出する関数を考えてみましょう:



コードをコピーします

コードは次のとおりです:


functionauthorized($username, $page) {
if (!isBlacklisted($username)) {
if (isAdmin($username)) {
return TRUE;
} elseif (isAllowed($username, $page)) {
return
} else {
return FALSE;
}
} else {
return FALSE;
}
}
?> 考慮すべきルールは 3 つだけであるため、この例は非常に単純です: 管理者は常にアクセスを許可され、
は常にブラックリストに登録されます。
isAllowed() は、他の人がアクセスできるかどうかを判断します。
(管理者がブラックリストに含まれている場合という特殊なケースもありますが、これは考えにくいため、ここではこの状況を無視します)。
コードをシンプルにし、ビジネス ロジックに重点を置くために、関数を使用してこの判断を行います。
例:



コードをコピーします

コードは次のとおりです:

function allowed($username, $page) {
if (!isBlacklisted($username)) {
if (isAdmin($username) || isAllowed($username, $page)) {
return TRUE;
} else {
return FALSE;
}
?>


実際には、次のように関数全体を複合条件に変換できます。コード

コードは次のとおりです:


function allowed($username, $page) { if (!isBlacklisted($username) && (isAdmin($username) || isAllowed($username, $page) ) {UReturn true; } else {Return false; function allowed($username, $page) {
return (!isBlacklisted($username) && (isAdmin($username) || isAllowed ($username, $page));
}
?>


ただし、コード行数を転写することが目的の場合は、 isBlacklisted( を使用していることに注意してください。 )、isAdmin()、および isAllowed() など、これらの判断に関係するものによっては、コードを単なる複合条件に減らすことは魅力的ではない可能性があります
次に、「即時復帰」関数という小さなトリックを説明します。できるだけ早く返すと、これらのルールを非常に簡単に表現できます。 :



コードをコピー
コードは次のとおりです:

function allowed($username, $page) { if ( isBlacklisted($username)) { return FALSE; if (isAdmin ($username)) {
return TRUE;
return isAllowed($username, $page)?>


;この例ではさらに多くのコード行が使用されていますが、非常に単純で魅力的ではありません。さらに重要なのは、このアプローチにより、考慮する必要があるコンテキストの量が減ります。たとえば、ユーザーがブラックリストに登録されているかどうかを判断した後は、そのことを忘れても問題ありません。これは、特にロジックが複雑な場合に非常に役立ちます。

4. 常に中括弧を使用してください
追伸: 許しは「括弧を削除する」ことを意味します
この記事の内容によれば、責任著者は「中括弧」ではなく「中括弧」を意味するはずです。 「括弧」は通常「角括弧」を意味します。中括弧がないと可読性と保守性が損なわれるため、このヒントは無条件に無視する必要があります。
簡単な例を示します:
コードをコピーします コードは次のとおりです:

if (date('d M') == '21 May')
$birthdays = array ('アル フランケン'、
'クリス シフレット'、
'クリス ウォレス'、
'ローレンス トゥロー');は次のとおりです:


if (date('d M') == '21 May')
$birthdays = array('Al Franken',
'Chris Shiflett',
'Chris Wallace' ,
'Lawrence Tureaud');
party(TRUE);
中括弧がないと、毎日社交的な集まりに参加することになります。おそらくあなたには忍耐力があるので、この間違いは歓迎されるでしょう。この愚かな例が、過度のパーティーが予期せぬ副作用をもたらす可能性があるという点から逸れないことを願っています。
中かっこをなくすことを推奨するために、以前の記事では例として次のような短いステートメントを使用しました:

コードをコピーします コードは次のとおりです:
if ($gollum == 'ハーフリング') $ height --;
else $height ++;


各条件が別の行に配置されているため、このエラーは発生する頻度が低いように見えますが、これにより別の問題が発生します。読んで理解するにはさらに時間がかかります。一貫性は非常に重要な特性であるため、開発者はコーディング標準自体が好きではない場合でも、コーディング標準に従うことがよくあります。
常に中かっこを使用することをお勧めします:


コードをコピーします
コードは次のとおりです:

if (date('d M') == '5月21日') {
$birthdays = array('アル・フランケン',
'クリス・シフレット',
'クリス・ウォレス',
'ローレンスTureaud');
party(TRUE);
?>

毎日パーティーをするのは問題ありませんが、必ず招待してください。 ereg_replace() と preg_replace() の代わりに str_replace() を使用してください

私たちは否定を聞くのは嫌いですが、(原文どおり) 誤用を実証するために使用されたこの小さなトリックは、回避しようとしていたのと同じ悪用問題を引き起こします。 (
軽蔑的に聞こえるのは嫌ですが、このヒントは、防止しようとしているのと同じ誤用につながる一種の誤解を示しています。) 文字列関数が文字マッチングにおいて正規表現関数より高速かつ効率的であることは明らかですが、著者はこれはひどいです 修正してください: 文字列関数のほうが正規表現関数よりも文字列のマッチングが速いというのは明白な事実ですが、このことから結果を導き出そうとする著者の試みは惨めに失敗します:
正規表現を使用している場合は、 ereg_replace() と preg_replace を使用してください。 () は str_replace() よりもはるかに高速です。
str_replace() はパターン マッチングをサポートしていないため、このステートメントには意味がありません。文字列関数と正規表現関数のどちらを選択するかは、どちらが速いかではありません。パターンを照合する必要がある場合は、正規表現関数を使用します。

6. トリプル演算子を使用します。最近実行した監査から抜粋した行:



コードをコピー コードは次のようになります:

$host = strlen($ host) > $host : htmlentities($host; ); ?>
ああ、作者の本当の意図は、文字列の長さが 0 より大きい場合に $host をエスケープすることでしたが、誤って逆のことをしてしまいました。犯しやすい間違いですよね?多分。コード監査中に見落とされやすいですか?確かに。シンプルだからといってコードが優れているとは限りません。
トリプル演算子は単一行、プロトタイプ、テンプレートにも適していますが、プレーンな条件文の方が常に優れていると考えられます。 PHP は記述的かつ詳細であり、コードも同様であるべきだと私たちは考えています。

7. Memcached

ディスクアクセスは遅く、ネットワークアクセスも遅く、データベースは通常両方を使用します。
記憶力が早い。ローカル キャッシュを使用すると、ネットワークとディスク アクセスのオーバーヘッドが回避されます。これらの原則を組み合わせると、もともと Perl ベースのブログ プラットフォーム LiveJournal 用に開発された「分散メモリ オブジェクト キャッシング システム」である memcached が完成します。
プログラムが複数のサーバーに分散されていない場合は、memcached は必要ない可能性があります。単一キャッシュ方法 - データをシリアル化し、一時ファイルに保存します。たとえば、リクエストごとに多くの冗長な作業を排除できます。実際、これはお客様のアプリケーションの最適化を支援する際に、私たちが考慮する簡単な成果です。
簡単に手に入る果物とは:
実のなる木には、動物や人間がそれほど苦労せずに届くほど低い枝がいくつかあることがよくあります。これらの低い枝に含まれる果物は、高いところにある果物ほど熟していない、または魅力的ではない可能性があります。これは、一般に、最小限の労力で最も簡単なターゲットを選択することを意味します。データをメモリにキャッシュする方法は、私たちの同僚である George Schlossnagle によって元々開発されたキャッシュ システムである APC の共有型ヘルパー メソッドを使用しています。次の例を考えてみましょう。 ?php $feed = apc_fetch('news');
if ($feed === FALSE) {
$feed = file_get_contents('http://example.org/news.xml');
// これを保存します5 分間のデータを共有メモリに保存します。
apc_store('news', $feed, 300);
// $feed で何かをします

;
このタイプのキャッシュを使用すると、リクエストごとにリモート サーバーがフィード データを送信するのを待つ必要がありません。多少の遅延が発生します。この例では上限は 5 分ですが、これはアプリケーションのニーズに応じてほぼリアルタイムに調整できます。
8. フレームワークを使用する
すべての決定には結果が伴います。私たちはフレームワークが大好きです。実際、CakePHP と Solar の主任開発者は OmniTI で私たちと協力しています。しかし、フレームワークを使用すると魔法のように物事が改善されるわけではありません。
10 月に、私たちの同僚の Paul Jones が HP Advent に「フランチャイズとしてのフレームワーク」という記事を書き、フレームワークを商用フランチャイズと比較しました。彼は、マイケル・ガーバーの著書「電子神話再訪」からのアドバイスを引用しています:
ガーバーは、ビジネスを成功させるには、起業家は自分のビジネスをフランチャイズとして販売するつもりであるかのように行動する必要があると指摘しています。これは、ビジネスオーナーがあらゆる決定に個人的に関与せずにビジネスを運営できる唯一の方法です。
( ガーバー氏は、ビジネスを成功させるには、起業家は自分のビジネスをフランチャイズのプロトタイプとして販売するつもりであるかのように行動する必要があると指摘しています。それが、ビジネスオーナーがあらゆることに個人的に関与することなくビジネスを運営できる唯一の方法です。 )
これは良い提案です。フレームワークを使用する予定がある場合でも、独自のタグや規則を定義する場合でも、将来の開発者の観点から価値を理解することが重要です。
私たちは、すべてに当てはまる真実をお伝えしたいと思っていますが、フレームワークが常に適切であることを示すためにアイデアを拡張することは、私たちがやりたいことではありません。
フレームワークを使用するべきかどうか尋ねられたら、私たちが与えることができる最善の答えは、「それは状況による」です。
9. エラー抑制演算子を正しく使用してください
常にエラー抑制演算記号の使用を避けるようにしてください。以前の記事で、著者は次のように述べています:
@ 演算子は非常に遅く、高パフォーマンスのコードを記述する必要がある場合は非常に高価になる可能性があります。
エラー抑制が遅いのは、PHP が抑制ステートメントを実行する前に error_reporting レベルを動的に 0 に変更し、その後すぐに復元するためです。これにはお金がかかります。
さらに悪いことに、エラー抑制機能を使用すると、問題の根本原因を追跡することが困難になります。
前の記事では、参照による変数への値の割り当てをサポートするために次の例を使用しました。 。 。 (この文をどう訳せばいいでしょうか?混乱しています~~~)
前の記事では、$albus が設定されているかどうかが不明な場合に、参照によって変数を割り当てる練習をサポートするために次の例を使用しています:
コードをコピー コードは次のとおりです:

$albert =& $albus;
?>

これは今のところ、奇妙な未定義の動作に依存していますが、次のような説明はありません。なぜこれが機能するのかをよく理解すると、バグが発生します。
$albert は $albus を参照しているため、後で $albus を変更すると $albert にも影響します
より良い解決策は isset() を使用して中括弧を追加することです:
コードをコピー コードは次のとおりです:

if (!isset($albus)) {
$albert = NULL;
?>

$albert に NULL を代入し、存在しない値を代入します。同じですが、より明確になっているため、コードの明瞭さが大幅に向上し、2 つの変数間の参照関係が回避されます。
エラー抑制演算子を過度に使用するコードを継承する場合は、エラー抑制を無効にする Scream という新しい PECL 拡張機能があります

これは実際には賢い方法ですが、前の記事ではまったく説明されていませんでした。以下は補足的な例です:

コードをコピー コードは次のとおりです:
if (isset($username[5])) {
// ユーザー名は少なくとも 6 つです
}
?>
文字列を配列として扱う場合 (荒野に光はありません。実際、C 言語では通常、文字は配列の形で存在します)、文字列内の各文字は配列の要素になります。特定の要素の有無を確認することで、文字列に少なくともその数の文字が存在するかどうかを確認できます。 (最初の文字は要素 0 であるため、$username[5] は $username の 6 番目の文字であることに注意してください。)
この方法で isset を使用する方が strlen よりわずかに高速である理由は複雑です。簡単に説明すると、strlen() は関数であり、isset() は構文構造です。一般に、関数の呼び出しは、言語構造を使用するよりもコストが高くなります。
著者について:
こんにちは、私たちは Chris Shiflett と Sean Coates です。私たちは 2 人とも OmniTI (「聞いたことのない最も重要な Web 会社」) で働いており、shiflett.org と seancoates で PHP やその他のことについてブログを書いています。 .com 、PHP Advent のキュレーション、@shiflett および @coates としての活動を行っています。
翻訳元: http://coding.smashingmagazine.com/2009/03/24/10-useful-php-tips-revisited/

http://www.bkjia.com/PHPjc/324064.htmlwww.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/324064.html技術記事 1. SQL インジェクションのチートシートを使用する 基本的な経験則は、ユーザーが送信したデータを決して信頼しないことです。 もう 1 つのルールは、データを送信または保存するときにデータをエスケープすることです (...
)
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。