[Force.comでWeb制作]Visualforceページのセキュリティ対策

By |11月 2, 2009|Force.comでWeb制作, |


クラウドで気になるポイントの一つが「セキュリティ」。
Force.com SitesでVisualforceページが外部に公開されるときにセキュリティはどのように担保されているのかを紹介。

参考:Security Tips for Apex and Visualforce Developers

[XSS対策]

XSSとは、例えばメールフォームなどのユーザがサーバに対して情報を送信できる部分に悪意のある記述をして送信することで、他のユーザやサーバに対して悪意のある行為ができる脆弱性のことです。この脆弱性があると例えば、ページの表示を書き換えたり他のユーザになりすましてサイトにログインしたり、色々悪意のある行為がされる危険性があります。

この対策として、基本的に全ての<apex>タグにはアンチXSSフィルタがかかっています。例えば<apex:outputtext>などのテキストを出力するタグは自動的にHTMLエスケープされます。この設定はescape属性で変更することも可能です。
例:

[php]
<apex:outputText escape="false" value="{!$CurrentPage.parameters.userInput}" />
[/php]

一方で、<apex>タグを使わずに自分でJavaScriptやHTMLコードを書いてしまうと、その部分のXSS対策は自分できちんと行う必要があります。Visualforceページには自分でHTMLやJavaScriptをごりごりと書くこともできますが、セキュリティ対策やその後々のサポートを考えると、できる限り<apex>タグを使っていた方が安全と言えそうです。

[Sコントロールと数式]

Sコントロールは全て自分でHTMLで書くのが前提なので、全てのHTML記述や数式タグの記述時にはそのSコントロールの開発者が責任もってアンチXSS処理を施す必要があります。例えば、{!$Request.*} という数式はURLのGETパラメータを取得できますが、この値をそのままページ内に記述してしまうと脆弱性につながるので、ページ内で使うときにはきちんとエスケープしてやる必要があります。

こんな感じ。

[php]
<html>
<head>
<title>{! SUBSTITUTE(SUBSTITUTE($Request.title,”<”,”<”),”>”,”>”)}</title>
</head>
<body>Hello world!</body>
</html>
[/php]

[CSRF]

クロスサイトリクエストフォージェリ。サイト閲覧者の意図しないサイトに勝手にアクセスさせて、その別のサイト上での勝手な動作をさせる攻撃手法です。例えばAというサイトにアクセスしているつもりが、その閲覧者が気づかないようにBというブログにアクセスさせられて勝手にコメントを付けている、とかそんなような動きをさせられます。悪用すると、その人のブラウザのクッキー情報を使ってその人のログイン情報を使いどこかのECサイトで買い物させる、とかいろんなことができてしまいます。

Force.com上では全ての標準ページについて、CSRF対策がされています。全てのページにはhidden項目でランダムな文字列が隠されており、次ページに遷移する際には常にその値がチェックされてそれが予期せぬ値である場合はエラーを返す仕様なので、例えばCSRFを利用して特定のレコード情報を特定の値に変更させられる、ということはありません。

一方で、VisualforceやAPEXを使って自分で作ったページについては、自分でCSRF対策を施す必要があります。例えば以下の例を見てみましょう。

[php]
<apex:page controller="myClass" action="{!init}"></apex:page>

public class myClass {
public void init() {
Id id = ApexPages.currentPage().getParameters().get(‘id’);
Account obj = [select id, Name FROM Account WHERE id = :id];
delete obj;
return ;
}
}
[/php]

この例だと、CSRFの飛び先として攻撃者の意図したレコードIDが指定されると、その特定のIDのレコード削除されてしまいます。これを応用すれば自分がログインしているSalesforce内に様々な操作がされてしまうことになります。

このようなケースに対してSalesforce標準の機能では防御できません。開発者がそのときの開発物ごとに動きを理解したうえで、常に気を配ってケアする必要があります。例えば対処策としては、ページ上で何らかのアクションを起こす場合には常に確認画面を挟んで[編集]→[確認]→[完了]というステップを踏ませるとか、もしくはSalesforceへのログインのセッション時間を短くしてSalesforceを操作するには頻繁にログイン認証を必要にしたり、同一ブラウザ使って別サイトを閲覧する際には必ずSalseforceを一度ログアウトさせるように徹底する、などの方法があります。

[SQLインジェクション]

XSSと同じように、例えばメールフォームなどのユーザがサーバに対して情報を送信できる部分に悪意のある記述をして送信することで不正なSQLコードを実行して、データベースに不正な操作を行う攻撃です。SQLインジェクションの脆弱性に関しては一般のWebサイトと同じです。標準オブジェクトのページではもちろん大丈夫ですが、APEXやVisualforceで作ったページについては開発者が対策する必要があります。

例えばこんなソースの場合:

[php]
<apex:page controller="SOQLController" >
<apex:form>
<apex:outputText value="Enter Name" />
<apex:inputText value="{!name}" />
<apex:commandButton value="Query" action="{!query}“ />
</apex:form>
</apex:page>

public class SOQLController {
public String name {
get { return name;}
set { name = value;}
}
public PageReference query() {
String qryString = ‘SELECT Id FROM Contact WHERE (IsDeleted = false and Name like \’%’ + name + ‘%\’) ‘ ;
queryResult = Database.query(qryString) ;
return null;
}
}
[/php]

インプットフィールドから受け取った「名前」のテキストをそのままSQOLで検索する、というページです。たとえば普通ここには「田中」とか入ってこんなSOQLができあがります。

[php]
sqyString = SELECT Id FROM Contact WHERE (IsDeleted = false and Name like ‘%田中%’)
[/php]

ここで例えばインプットフィールドに

[php]
name = test%’) or (Name like ‘
[/php]

のように書いたとすると、実行されるSOQL文は

[php]
SELECT Id FROM Contact WHERE (IsDeleted = false and Name like ‘%test%’) or (Name like ‘%’)
[/php]

となり、結果としてContactの全レコード情報を取得できてしまいます。

対策:
この例への対策を施したコードは以下のようになります。

[php]
public class SOQLController {
public String name {
get { return name;}
set { name = value;}
}
public PageReference query() {
String queryName = ‘%’ + name + ‘%’
queryResult = [SELECT Id FROM Contact WHERE (IsDeleted = false and Name like :queryName)];
return null;
}
}
[/php]

動的にSQOL文を生成するのではなく、静的なSOQL文の検索条件値に受け取ったインプットフィールドの値を文字列としてバインドすることで対処しています。

この他の対策として、どうしても動的にSOQL文を生成する必要がある場合には、escapeSingleQuotesメソッドを使ってインプットフィールドから受け取った文字列のシングルクォーテーションを必ずエスケープする方法もあります。

[データアクセスコントロール]

Force.com上では複雑なセキュリティコントロールが設定できて、個別のユーザごとに様々な権限を割り当てることができます。しかし、APEXスクリプトはデフォルトで全レコードの参照/編集権限をもっています。そのため例えばあるレコードの参照権限をもっていないユーザがなんらかのVisualforceページで検索を実行したときに、本来見ることができないはずのレコードが表示されるという自体がおきます。

[php]
public class customController {
public void read() {
Contact contact = [Select id from Contact where Name = :value];
}
}
[/php]

これを回避するためにはAPEXクラスにwith sharingというキーワードをつけます。

[php]
public with sharing class customController {
. . .
}
[/php]

こうすることで、このAPEXクラスで実行される権限がそのときログインしているユーザ権限になり、このクラス内から返されるレコードもそのユーザの権限に連動して設定してあるセキュリティの範囲でのレコード操作だけでできることになります。