セキュアなWebアプリケーションを構築するための第一歩は、入力値チェックを確実に行うことである。悪意あるデータを受け付けない実装は、データの不整合を防ぐだけでなく、プログラムの安全性を高めることにつながる。
Strutsでは、日付フィールドなどの範囲チェックや、必須チェック、形式チェックなどさまざまな入力値チェックを容易に実装するために、Validatorプラグインという仕組みが提供されている。
【参考記事】
Validatorによる妥当性検証の実現(前編)(Java Solution)
今回は、Struts 1.2.8におけるValidatorプラグインの仕組みをセキュリティ側から考察し、使用上の注意点を探っていく。
Validatorプラグインの仕組み
まず、Validatorプラグインを使用したサンプルアプリケーションを作成して、動作の仕組みを見ていこう。
前略
<action
path="/RegistUser" type="vlunApp.RegistUserAction"
validate="true" -(1)
name="UserForm" -(2)
scope="request" input="/pages/registUser.jsp">
<forward name="success" path="/pages/registUserComp.jsp" />
</action>
後略
前略
<form name="UserForm"> -(3)
<field property="userId" depends="required,mask">
<arg key="UserForm.userId"/>
<var>
<var-name>mask</var-name><var-value>^[0-9a-zA-Z]*$</var-value>
</var>
</field>
<field property="password" depends="required,mask">
<arg key="UserForm.password"/>
<var>
<var-name>mask</var-name> <var-value>^[0-9a-zA-Z]*$</var-value>
</var>
</field>
</form>
後略
public class RegistUserAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
UserForm userForm = (UserForm) form;
String userId = userForm.getUserId();
String password = userForm.getPassword();
String sql = "insert into m_user (user_id,password) values ('"
+ userId + "','" + password + "')";
DBMgr db = new DBMgr();
Connection conn = db.getConnection();
Statement stmt = conn.createStatement();
try {
stmt.executeUpdate(sql);
conn.commit();
} finally {
中略
}
return mapping.findForward("success");
}
}
public class UserForm extends ValidatorForm {
private String userId;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
}
作成したサンプルアプリケーション上で、UserIDに「'(シングルクオーテーション)」を入力して、OKボタンを押した際の挙動は以下となる。UserIDが形式チェックされていることが分かるだろう。
このサンプルアプリケーションにおいて、Validatorに関連している各設定の意味を以下に記述する。
- (1)action要素のvalidator属性にtrueを設定することで、検証機能(入力値のチェック機能)が有効となる
- (2)ActionパスとValidatorFormが関連付けられる
- 検証の定義を持つValidatorFormを継承して作成したFormクラスと、Actionパスが関連付けられる。
- (3)ValidatorFormと検証ルールが関連付けられる
- ここでは、必須チェックと形式チェックを行っている。
上記の定義を基に検証処理を制御しているのが、RequestProcesser#processValidateメソッドである。
protected boolean processValidate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
if (form == null) { -(4)
return (true);
}
// Was this request cancelled?
if (request.getAttribute(Globals.CANCEL_KEY) != null) { -(5)
return (true);
}
// Has validation been turned off for this mapping?
if (!mapping.getValidate()) { -(6)
return (true);
}
// Call the form bean's validation method
ActionMessages errors = form.validate(mapping, request);
if ((errors == null) || errors.isEmpty()) { -(7)
return (true);
}
中略
return (false);
}
RequestProcesser#processValidateメソッドは、検証処理の結果が妥当であればtrueを返し、妥当でない場合falseを返す動作をする。
- (4)struts-config.xmlにおいて、action要素のname属性が設定されていない場合は、検証を行わずにtrueを返す
- (5)Globals.CANCEL_KEYという属性がリクエストスコープに存在した場合は、検証を行わずにtrueを返す
- (6)struts-config.xmlにおいて、action要素のvalidate属性がtrueでない場合は、検証を行わずにtrueを返す
- (7)検証を行った結果が妥当であった場合は、trueを返す
(4)、(6)、(7)については、各設定ファイルの定義を反映した挙動である。ただし、(5)の動作は設定ファイルに定義されているものではない。Globals.CANCEL_KEYという属性が存在した場合、無条件にtrueを返すように読み取れる挙動はどのような意図で実装されているのだろうか。
Copyright © ITmedia, Inc. All Rights Reserved.