Java
spring-boot
SpringBoot

SpringBootを使うときに最低限やっておきたいセキュリティ対策

More than 3 years have passed since last update.

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なら以下のように特定のパスへのリクエストを拒否するようにすれば良いでしょう。


nginx.confより一部抜粋

        ・・・

location /health {
return 404;
}
・・・


X-Application-ContextをHTTPレスポンスヘッダに付与させない

actuatorを有効にすると、全てのHTTPリクエストに対するレスポンスヘッダに"X-Application-Context"ヘッダが付与されるようになります。

このヘッダ情報は、アプリケーションを稼働しているProfile情報とListenポート番号を暴露するため、できれば無効化しておきたいところです。


対策案1. SpringBootの設定で無効化する

SpringBoot 1.1.5以上であればapplication.propertiesに以下の設定を追加することで、無用なヘッダ情報が付加されるのを防ぐことができます(YAMLでも可)。


application.properties

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を返すので、実装次第では内部情報を推測できる情報が暴露されることになります。


エラー例.json

{

"timestamp" : 1426615606,
"exception" : "org.springframework.web.bind.MissingServletRequestParameterException",
"status" : 400,
"error" : "Bad Request",
"path" : "/welcome",
"message" : "Required String parameter 'name' is not present"
}

出典:http://stackoverflow.com/questions/29106637/modify-default-json-error-response-from-spring-boot-rest-controller

設定次第ではスタックトレースまで吐き出してくれるので、せめてエラーの事実までに留めて詳細情報は表示させたくないところです。


対策案. ErrorAttributesを独自に実装する

そのアプリケーションの特性に応じたレスポンスを設計するのがよいと思いますが、ここではDefaultErrorAttributesを継承したカスタムクラスを作って、最低限外部には晒してほしくない情報を返さないようにしてみました。


CustomErrorAttributes.java

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定義を上記で作成したクラスで上書きます。


Hogeconfig.java


・・・
@Bean
public ErrorAttributes errorAttributes() {
return new CustomErrorAttributes();
}
・・・


内部情報を推測できる情報は返さなくなったので、デフォルトよりはマシになったと言えるでしょう。


CustomErrorAttributesの返却例.json

{

timestamp: 1440346172908
status: 404
error: "Not Found"
}