投稿日:
【SpringBoot2】エラーレスポンスを独自カスタマイズする方法【DefaultErrorAttributes】
YouTubeも見てね♪
ねこじゃすり
猫を魅了する魔法の装備品!
【最新機種】GoPro hero11 Black
最新機種でVlogの思い出を撮影しよう!
[ノースフェイス] THE NORTH FACE メンズ アウター マウンテンライトジャケット
防水暴風で耐久性抜群なので旅行で大活躍です!
レッドブル エナジードリンク 250ml×24本
翼を授けよう!
ドラゴンクエスト メタリックモンスターズギャラリー メタルキング
みんな大好き経験値の塊をデスクに常備しておこう!
Bauhutte ( バウヒュッテ ) 昇降式 L字デスク ブラック BHD-670H-BK
メインデスクの横に置くのにぴったりなおしゃれな可動式ラック!
MOFT X 【新型 ミニマム版】 iPhone対応 スマホスタンド
Amazon一番人気のスマホスタンド!カード類も収納出来てかさ張らないのでオススメです!
目次
SpringBoot2のデフォルトのエラーレスポンスは危険
脆弱性の原因
SpringBoot2でシステム開発をする際は、APIを開発する事が多いと思います。
@RestController
をつけることで簡単に実装を進める事が出来るので、とても便利なフレームワークですよね。
しかし、何も気にせずそのまま実装を進めると、システムエラーが発生した際に、システム内部の情報が含まれたレスポンスが返却されてしまうので、脆弱性に繋がる可能性があります。
デフォルトのエラーレスポンス
実際にサーバー内部でエラーが起きると以下のようなレスポンスが返ります。
1
2
3
4
5
6
7
8
|
{
"timestamp": "2019-04-23T09:00:46.014+0000",
"status": 500,
"error": "Internal Server Error",
"message": "No message available",
"trace": "java.lang.NullPointerException\n\tat tech.blogenist.service.account.api.service.account.detail.AccountDetailService.findBy(AccountDetailService.java:23)\n\tat tech.blogenist.service.account.api.controller.account.detail.AccountDetailController.detail(AccountDetailController.java:28)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.lang.reflect.Method.invoke(Method.java:498)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:634)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.lang.Thread.run(Thread.java:745)\n",
"path": "/v1/accounts/999/"
}
|
パッケージ情報やエラー詳細、場合によってはパラメーターやDBのデータも出力されてしまうためとても危険です。
カスタマイズ可能
そのままだと危険ですが、もちろんエラーレスポンスをカスタマイズすることが出来ます。
今回はそのやり方をご紹介致します。
手順
DefaultErrorAttributesの継承クラスを用意する
やり方はとても簡単です。
以下のようなDefaultErrorAttributes
の継承クラスを用意して、getErrorAttributes
メソッドをOverride
すれば良いだけです。
今回は試しにpath
とtimestamp
だけを返すようにしてみました。@Component
を忘れずに!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package tech.blogenist.service.account.api.infrastructure.error.attributes;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
@Component
public class CustomErrorAttributes extends DefaultErrorAttributes
{
public static final String TIME_STAMP = "timestamp";
public static final String PATH = "path";
private final String PATH_ATTRIBUTE = "javax.servlet.error.request_uri";
@Override
public Map< String, Object > getErrorAttributes( WebRequest webRequest, boolean includeStackTrace )
{
Map< String, Object > customErrorAttribute = new HashMap< String, Object >();
String path = ( String ) webRequest
.getAttribute( PATH_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST );
customErrorAttribute.put( TIME_STAMP, OffsetDateTime.now() );
customErrorAttribute.put( PATH, path );
return customErrorAttribute;
}
}
|
確認
では、もう一度同じエラーを発生させてみましょう。
1
2
3
4
|
{
"path": "/v1/accounts/999/",
"timestamp": "2019-04-23T15:08:47.866+0000"
}
|
しっかりとカスタムエラーレスポンスが適用されるようになり、システム内部のエラーの詳細が返却されないようになりましたね♪
参考
Spring Boot 2 プログラミング入門
終わりに
パッケージ情報やエラー詳細は脆弱性やハッキングに繋がるので必ずレスポンスに含まれないようにしましょう。
また、レスポンス項目も自由に追加することが可能なので、要件にあったエラーレスポンスを考えてみてください♪