SpringBootを使うと書くコード量が激減して大変便利ですが、デフォルトの定義に頼りすぎると意外なところで内部情報をサクッと暴露してくれたりするので油断できません。
そこで、SpringBootを弄ってみて気がついたリスキーな箇所とその対応策を挙げていきたいと思います。
(あくまで気がついた範囲なので、全部カバーできているわけではないです!)
Spring Boot Actuator編
Actuatorを有効化すると様々な情報がREST APIとして提供されるので大変便利ですが、内部情報を晒したりシャットダウンすることも可能になるので外部からは見えないように対策したいところです。
使用するなら外部からはアクセスできないようにする
- SpringBootアプリケーションがlistenするポート番号を直接外部に公開しない
- アプリケーションへのアクセスは、Webサーバを経由するようにし、且つActuatorのパスに対するアクセスをブロックする
/shutdown
/trace
/autoconfig
/configprops
/metrics
/metrics/*
/mappings
/info
/health
/env
/env/*
/beans
/dump
例えば、nginxなら以下のように特定のパスへのリクエストを拒否するようにすれば良いでしょう。
・・・
location /health {
return 404;
}
・・・
X-Application-ContextをHTTPレスポンスヘッダに付与させない
actuatorを有効にすると、全てのHTTPリクエストに対するレスポンスヘッダに"X-Application-Context"ヘッダが付与されるようになります。
このヘッダ情報は、アプリケーションを稼働しているProfile情報とListenポート番号を暴露するため、できれば無効化しておきたいところです。
対策案1. SpringBootの設定で無効化する
SpringBoot 1.1.5以上であればapplication.properties
に以下の設定を追加することで、無用なヘッダ情報が付加されるのを防ぐことができます(YAMLでも可)。
management.add-application-context-header=false
対策案2. Webサーバで無効化する
本来であればSpringBoot側の設定変更でどうにかしたいところですが、古いバージョンを使っていたり、何らかの事情でどうしてもアプリケーション側で対応できない場合は、プロキシとして使用しているWebサーバ側での対処を検討します。
利用するWebサーバに依存するところではありますが、nginxであればHttpHeadersMoreModule
、Apacheであればmod_headers
を利用してX-Application-Context
ヘッダ情報を書き換えるよいでしょう。
参考
http://docs.spring.io/spring-boot/docs/1.2.5.RELEASE/reference/htmlsingle/#production-ready
https://github.com/spring-projects/spring-boot/issues/1308
http://wiki.nginx.org/HttpHeadersMoreModule
http://httpd.apache.org/docs/2.2/en/mod/mod_headers.html
http://httpd.apache.org/docs/2.4/en/mod/mod_headers.html
RestController編
デフォルトのエラーレスポンスは使わない
@RestController
を使用したControllerへの不適切なアクセスを行うと、ErrorAttribute
インタフェースの実装クラスを返します。
SpringBootは、特に設定をしないとデフォルトでorg.springframework.boot.autoconfigure.web.DefaultErrorAttributes
を返すので、実装次第では内部情報を推測できる情報が暴露されることになります。
{
"timestamp" : 1426615606,
"exception" : "org.springframework.web.bind.MissingServletRequestParameterException",
"status" : 400,
"error" : "Bad Request",
"path" : "/welcome",
"message" : "Required String parameter 'name' is not present"
}
設定次第ではスタックトレースまで吐き出してくれるので、せめてエラーの事実までに留めて詳細情報は表示させたくないところです。
対策案. ErrorAttributesを独自に実装する
そのアプリケーションの特性に応じたレスポンスを設計するのがよいと思いますが、ここではDefaultErrorAttributes
を継承したカスタムクラスを作って、最低限外部には晒してほしくない情報を返さないようにしてみました。
public class CustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, requestAttributes);
// 単に詳細情報を出力させないようにしただけです
// addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
// addPath(errorAttributes, requestAttributes);
return errorAttributes;
}
// 継承元の実装をそのまま踏襲しています。
private void addStatus(Map<String, Object> errorAttributes,
RequestAttributes requestAttributes) {
Integer status = getAttribute(requestAttributes,
"javax.servlet.error.status_code");
if (status == null) {
errorAttributes.put("status", 999);
errorAttributes.put("error", "None");
return;
}
errorAttributes.put("status", status);
try {
errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
}
catch (Exception ex) {
// Unable to obtain a reason
errorAttributes.put("error", "Http Status " + status);
}
}
@SuppressWarnings("unchecked")
private <T> T getAttribute(RequestAttributes requestAttributes, String name) {
return (T) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
}
}
Bean定義を上記で作成したクラスで上書きます。
・・・
@Bean
public ErrorAttributes errorAttributes() {
return new CustomErrorAttributes();
}
・・・
内部情報を推測できる情報は返さなくなったので、デフォルトよりはマシになったと言えるでしょう。
{
timestamp: 1440346172908
status: 404
error: "Not Found"
}