Spring Securityが投げるRequestRejectedExceptionをハンドリングする
やりたいこと
Spring Security 5.7.10を使用しています。
Spring Securityはデフォルトで StrictHttpFirewall を使用して、下記に挙げるような不正っぽいリクエストを拒否してくれます。
- 許可していないHTTPメソッドを使用したリクエスト
- 正規化されていないURL
- URLにセミコロンを含む
2023-09-08 12:19:15.611 ERROR 9276 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlocklistedUrls(StrictHttpFirewall.java:535)
at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:505)
// 省略
拒否した場合は、RequestRejectedException
を投げて500エラーとなります。
バグなどに気づきやすいようにエラーをSlack等に通知している場合、この種のエラーはノイズになりがちです。
例外をハンドリングしてINFOレベルでログに出力できないか試しました。
やってみる
Controllerに到達する前にエラーとなるので、@ExceptionHandler
を使用して例外ハンドラーを定義しても機能しません。
Spring Securityが用意している RequestRejectedHandler
を実装して、Beanを登録することで、例外をハンドリングできます。
Spring Securityが用意している HttpStatusRequestRejectedHandler
を参考に CustomHttpStatusRequestRejectedHandler
を作成します。
httpError
でステータスコードを、handle()
で例外発生時の処理を定義しています。
public class CustomHttpStatusRequestRejectedHandler implements RequestRejectedHandler {
private static final Log logger = LogFactory.getLog(CustomHttpStatusRequestRejectedHandler.class);
private final int httpError;
/**
* Constructs an instance which uses {@code 400} as response code.
*/
public CustomHttpStatusRequestRejectedHandler() {
this.httpError = HttpServletResponse.SC_BAD_REQUEST;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
RequestRejectedException requestRejectedException) throws IOException {
logger.info(LogMessage.format("Rejecting request due to: %s", requestRejectedException.getMessage()),
requestRejectedException); // ログレベルはINFO
response.sendError(this.httpError); // 400エラーとしてレスポンスする
}
}
続いて、作成したハンドラーをBean登録します。
@Bean
public RequestRejectedHandler requestRejectedHandler() {
return new CustomHttpStatusRequestRejectedHandler();
}
やることはこれだけです。
試しに http://localhost:8080/;
にリクエストを投げると下記のようにINFOレベルでログが出力されました🙌
2023-09-08 11:48:16.825 INFO 89178 --- [nio-8080-exec-1] w.CustomHttpStatusRequestRejectedHandler : Rejecting request due to: The request was rejected because the URL contained a potentially malicious String ";"
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlocklistedUrls(StrictHttpFirewall.java:535)
at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:505)
// 省略
私たち BABY JOB は、子育てを取り巻く社会のあり方を変え、「すべての人が子育てを楽しいと思える社会」の実現を目指すスタートアップ企業です。圧倒的なぬくもりと当事者意識をもって、こどもと向き合う時間、そして心のゆとりが生まれるサービスを創出します。baby-job.co.jp/
Discussion