ServerWebExchangeFirewall

アプリケーションを悪用する悪意のあるユーザーによってリクエストが作成される方法はさまざまです。Spring Security は、悪意があると思われるリクエストを拒否できるように ServerWebExchangeFirewall を提供します。デフォルトの実装は、悪意のあるリクエストを拒否する StrictServerWebExchangeFirewall です。

たとえば、リクエストにはパストラバーサルシーケンス ( /../ など) や複数のスラッシュ (//) が含まれる場合があり、これらもパターン一致の失敗の原因となる可能性があります。コンテナーによっては、サーブレットマッピングを実行する前にこれらを正規化しますが、そうしないものもあります。このような課題を防ぐために、WebFilterChainProxy は ServerWebExchangeFirewall 戦略を使用してリクエストをチェックし、ラップします。デフォルトでは、正規化されていないリクエストは自動的に拒否され、一致のためにパスパラメーターが削除されます。(したがって、たとえば、元のリクエストパス /secure;hack=1/somefile.html;hack=2 は /secure/somefile.html として返されます) したがって、WebFilterChainProxy を使用することが不可欠です。

実際には、Web アプリケーションレベルで定義されたセキュリティ制約の使用に全面的に依存するのではなく、サービスレイヤーでメソッドセキュリティを使用してアプリケーションへのアクセスを制御することをお勧めします。URL は変化するため、アプリケーションがサポートする可能性のあるすべての URL と、リクエストの操作方法を考慮するのは困難です。理解しやすいいくつかの単純なパターンを使用するように制限する必要があります。常に「デフォルトで拒否」アプローチを使用するようにしてください。このアプローチでは、アクセスを拒否するために最後にキャッチオールワイルドカード (/ または ) を定義します。

サービス層で定義されたセキュリティは、はるかに堅牢であり、迂回するのが難しいため、Spring Security のメソッドセキュリティオプションを常に利用する必要があります。

ServerWebExchangeFirewall を Bean として公開することでカスタマイズできます。

マトリックス変数を許可
  • Java

  • Kotlin

@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
    firewall.setAllowSemicolon(true);
    return firewall;
}
@Bean
fun httpFirewall(): StrictServerWebExchangeFirewall {
    val firewall = StrictServerWebExchangeFirewall()
    firewall.setAllowSemicolon(true)
    return firewall
}

クロスサイトトレース (XST) [OWASP] (英語) および HTTP 動詞の改ざん [OWASP] (英語) から保護するために、StrictServerWebExchangeFirewall は、許可されている有効な HTTP メソッドの許可されたリストを提供します。デフォルトの有効なメソッドは DELETEGETHEADOPTIONSPATCHPOSTPUT です。アプリケーションで有効なメソッドを変更する必要がある場合は、カスタム StrictServerWebExchangeFirewall Bean を構成できます。次の例では、HTTP GET および POST メソッドのみを許可しています。

GET&POST のみを許可
  • Java

  • Kotlin

@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
    firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
    return firewall;
}
@Bean
fun httpFirewall(): StrictServerWebExchangeFirewall {
    val firewall = StrictServerWebExchangeFirewall()
    firewall.setAllowedHttpMethods(listOf("GET", "POST"))
    return firewall
}

HTTP メソッドを許可する必要がある場合(非推奨)、StrictServerWebExchangeFirewall.setUnsafeAllowAnyHttpMethod(true) を使用できます。これを行うと、HTTP メソッドの検証が完全に無効になります。

StrictServerWebExchangeFirewall は、ヘッダー名と値、パラメーター名もチェックします。各文字には、制御文字ではなく、定義されたコードポイントが必要です。

この要件は、次の方法を使用して、必要に応じて緩和または調整できます。

  • StrictServerWebExchangeFirewall#setAllowedHeaderNames(Predicate)

  • StrictServerWebExchangeFirewall#setAllowedHeaderValues(Predicate)

  • StrictServerWebExchangeFirewall#setAllowedParameterNames(Predicate)

パラメーター値は setAllowedParameterValues(Predicate) で制御することもできます。

例: このチェックをオフにするには、StrictServerWebExchangeFirewall を常に true を返す Predicate インスタンスに接続できます。

任意のヘッダー名、ヘッダー値、パラメーター名を許可する
  • Java

  • Kotlin

@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
    firewall.setAllowedHeaderNames((header) -> true);
    firewall.setAllowedHeaderValues((header) -> true);
    firewall.setAllowedParameterNames((parameter) -> true);
    return firewall;
}
@Bean
fun httpFirewall(): StrictServerWebExchangeFirewall {
    val firewall = StrictServerWebExchangeFirewall()
    firewall.setAllowedHeaderNames { true }
    firewall.setAllowedHeaderValues { true }
    firewall.setAllowedParameterNames { true }
    return firewall
}

または、許可する必要のある特定の値がある場合があります。

例: iPhone X ʀ は、ISO-8859-1 文字セットにない文字を含む User-Agent を使用します。このため、一部のアプリケーションサーバーはこの値を 2 つの別々の文字に解析し、後者は未定義の文字になります。

これは、setAllowedHeaderValues メソッドで対処できます。

特定のユーザーエージェントを許可する
  • Java

  • Kotlin

@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
    Pattern allowed = Pattern.compile("[\\p{IsAssigned}&&[^\\p{IsControl}]]*");
    Pattern userAgent = ...;
    firewall.setAllowedHeaderValues((header) -> allowed.matcher(header).matches() || userAgent.matcher(header).matches());
    return firewall;
}
@Bean
fun httpFirewall(): StrictServerWebExchangeFirewall {
    val firewall = StrictServerWebExchangeFirewall()
    val allowed = Pattern.compile("[\\p{IsAssigned}&&[^\\p{IsControl}]]*")
    val userAgent = Pattern.compile(...)
    firewall.setAllowedHeaderValues { allowed.matcher(it).matches() || userAgent.matcher(it).matches() }
    return firewall
}

ヘッダー値の場合、代わりに検証時に UTF-8 として解析することを検討できます。

ヘッダーを UTF-8 として解析する
  • Java

  • Kotlin

firewall.setAllowedHeaderValues((header) -> {
    String parsed = new String(header.getBytes(ISO_8859_1), UTF_8);
    return allowed.matcher(parsed).matches();
});
firewall.setAllowedHeaderValues {
    val parsed = String(header.getBytes(ISO_8859_1), UTF_8)
    return allowed.matcher(parsed).matches()
}

ServerExchangeRejectedHandler インターフェースは、Spring Security の ServerWebExchangeFirewall によってスローされた ServerExchangeRejectedException を処理するために使用されます。デフォルトでは、リクエストが拒否されたときにクライアントに HTTP 400 レスポンスを送信するには HttpStatusExchangeRejectedHandler が使用されます。動作をカスタマイズするには、ユーザーは ServerExchangeRejectedHandler Bean を公開できます。例: 次の例では、リクエストが拒否されたときに HTTP 404 が送信されます。

リクエストが拒否された場合に 404 を送信する
  • Java

  • Kotlin

@Bean
ServerExchangeRejectedHandler rejectedHandler() {
	return new HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND);
}
@Bean
fun rejectedHandler(): ServerExchangeRejectedHandler {
    return HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND)
}

カスタム ServerExchangeRejectedHandler 実装を作成することにより、処理を完全にカスタマイズできます。