1

(検索エンジンを使用してもこれを簡単に見つけることができなかったので、いくつかの (できれば役立つ) 情報にアクセスできるようにするために、この質問をして (そして答えて) います。しかし、気軽に答えて有用な情報を追加してください :-))

PythonでHTTPヘッダーをどのようにエスケープ/引用できますか?

または、コンテキストエスケープ値が含まれていないことを確認するために、どのように検証できますか?

つまり、HTTP ヘッダーに対してはどのようにすればよいの でしょうか? HTML と URL に対してはcgi.escapeurllib.quoteメソッド (およびサニタイズ) は何を行うのでしょうか? これは、 HTTP ヘッダー インジェクションや同様の悪用を防ぐために使用できます。

例えば...

リダイレクト先の URL を提供するユーザーがいます。インジェクション攻撃 (よく知られているのはSQL インジェクションです) から保護したいと考えています。(この議論のために) セキュリティ上の懸念 (ユーザーが選択できるドメイン内の URL への密かに自動転送に関する) はさておき、ヘッダーを使用しLocation:てリダイレクトすることにした場合、HTTP ヘッダーを防ぐためにユーザーが指定した URL をどのようにエスケープできますか?インジェクション (または HTTP にとって危険な値が含まれているかどうかを検出します)?

# on a "posix sh"-like command-line...
# ...(it contains a malicious HTTP value)
$ redirect_to 'http://example.com'"\r\n"'Set-Cookie: malicious=value'

ここで、コマンドを実装する Python コードでredirect_to、上記のように入力して、エスケープする (無害にする) か、エラーにする必要があります。どうすればそうできますか?

4

2 に答える 2

1

入力データがヘッダー フィールドパラメーター(たとえば、ヘッダーのfilenameパラメーター) に含まれている場合、( Content-Dispositionrfc2231 エンコーディングのバリエーションを定義するこれらの仕様email.utils.encode_rfc2231によって制約されるように) でエンコードできます。

ヘッダー フィールド パラメータに含まれていない場合、このメソッドは使用できないようです。このような状況では、 Julian Reschke が書いたように、入力を含めないことが最も安全な方法でしょう。ただし、どうしても入力を含める場合は、次のいずれかの方法を試してください。

( HTTP は MIME 準拠のプロトコルではないため、これは安全ではない可能性があります。したがってMIME-Versionヘッダーが使用されない限り(そしておそらく使用されていても?)、これらの方法はHTTP では正しく機能しない可能性があります。)

一方通行...

これを行うには、完全に確実ではないかもしれませんが (**編集**: (単独で使用した場合) 絶対確実ではありません; ヘッダーを終了し、ヘッダーを開始する `\r\n\r\n` を受け入れます本文! したがって、`\r` および `\n` は、`\r`/`\n` 以外の空白 (タブやスペースなど) が前にない限り、処理する必要があります)、`email.header`を使用することです。モジュール。これは特にrfc822 ヘッダー用に設計されています (**編集**: しかし (どうやら、email パッケージは以前はいくつかの別個のモジュール ( example ))であったため、HTTP ヘッダー用ではありません! )、そのためのツールのようです。この `Header` クラスは、完全な `Header-Name: value` ではなく、ヘッダーの *values* をエンコードすることを目的としているため、このジョブの候補です (値 *のみ* を検証またはエスケープする場合)。

(ヒント: モジュール内のツールの多くは、他の MIME 形式 ( editemail : およびおそらく MIME に似たもの)を操作するときにも便利です。モジュール内のもの、特に HTTP フォームの解析にはあまりにも便利です。)cgicgi.FieldStorage

ただし、入力に悪意があると思われる (別の (埋め込まれた) ヘッダーが含まれているように見える)email.header場合にのみエラーが発生します。ただし、無効な入力をエスケープして処理しないようです (そうでない場合は、コメントでこれを修正してください)。(パラメーターはヘッダー フラグメントをエスケープし、有効な入力を返す必要がありますが、ユーザー エージェント (電子メール、HTTP など) との互換性があまり高くない場合があります。ここを参照してください( edit : <a href="https://stackoverflow .com/a/1361646/541412>多くの HTTP ユーザー エージェントがサポートしています (必ずしもクラスのエンコーディングのパラメーターではありません(rfc2231 エンコーディング以外に MIME 固有のエンコーディングを使用しているようです)、しかし) rfc5987charsetcharsetemail.header.Headerエンコーディング)。

例:

import email.header
import re

def check_string_for_rfc822_header(s):
    wip_header_component = str(email.header.Header(s))
    if re.search(r'(\r?\n[\S\n\r]|\r[\S\r])', wip_header_component):
        raise Exception
    else:
        return wip_header_component

# testing...
>>> check_string_for_rfc822_header("aaa")
"aaa"
>>> check_string_for_rfc822_header("a\r\nb")
"a\r\nb"
>>> check_string_for_rfc822_header("a\r\nb: c")
<error>

別の方法...

これを行うには、単に`\r` と `\n` 文字を削除するだけのようです (ただし、それぞれ個別に; 完全な文字列 `\r\n` の出現を削除しないでください。別々に発生し、多くの (ほとんどの?) HTTP ユーティリティはそれぞれを別々に受け入れます!)。同様に、`\r\n`、`\r`、および `\n` を先頭に空白を追加して置き換えることにより、ヘッダーをエスケープできます (これはヘッダーをエスケープする方法です。標準を参照してください)。

ただし、この方法では標準の詳細が考慮されていないため (たとえば、rfc822 ヘッダーは ACSII である必要があります)、単独で悪用される可能性があります。

例:

def remove_linebreakers(s):
    return s.replace("\n", "").replace("\r", "")

# or...
import re

def remove_linebreakers(s):
    re.sub(r'[\n\r]', '', s)


# testing...
>>> remove_linebreakers("aaa")
"aaa"
>>> remove_linebreakers("a\r\nb")
"ab"
>>> remove_linebreakers("a\r\nb: c")
"ab: c"

要約すれば...

最初の方法の方が良いように見えますが、それがパラメーター値でない限り (エスケープではなく) 検証のためだけです

例:

# if we are not working with a header param value, the following...
# ...raises email.errors.HeaderParseError if input is poisonous when in a header
wip_header_component = str(email.header.Header('<input>'))
header_component = (raise_error() if re.search(r'(\r?\n[\S\n\r]|\r[\S\r])', wip_header_component) else wip_header_component)
# ...or if we *are* working with a header param value...
email.utils.encode_rfc2231('<input>', 'UTF-8')
于 2013-10-28T07:06:45.137 に答える