42
Visualforceセキュアコーディング 初級編 2014/11/05 中村

8 power night2014_nakamura

Embed Size (px)

Citation preview

Visualforceで

セキュアコーディング

初級編

2014/11/05 中村

ねらい

• Dreamforceセッション

「Secure Coding:

Field-level Security, CRUD, and Sharing」

より、

今回は、画面開発に絞って、CRUDとFLSを遵守する術を再度確認します。

2

3

#1 SF設定において データアクセスを、どう 権限管理するの~

データに対するセキュリティ設定

4

1. オブジェクトレベルのアクセス権限

2. 項目レベルのセキュリティ

3. Force.comでの共有設定

を適切に設定する。

データに対するセキュリティ設定

CRUD

5

1. オブジェクトレベルのアクセス権限

2. 項目レベルのセキュリティ

3. Force.comでの共有設定

を適切に設定する。

FLS

Sharing

データに対するセキュリティ設定

6

CRUD

• Read

• Create

• Edit

• Delete

1. オブジェクトレベルのアクセス権限

プロファイルごとに設定・管理する。

データに対するセキュリティ設定

2. 項目レベルのセキュリティ

7

プロファイルごとに設定・管理する。

• Required

• Editable

• Read-Only

FLS

データに対するセキュリティ設定

3. Force.comでの共有設定

8

今日は割愛します。ごめんなさい。

Sharing

データに対するセキュリティ設定

CRUD

9

1. オブジェクトレベルのアクセス権限

2. 項目レベルのセキュリティ

3. Force.comでの共有設定

を適切に設定する。

FLS

Sharing

10

#2 ページ開発時に、何を 気をつければいいの~

まず、大前提として・・・

Apex:

Apex Classes do not enforce CRUD

Why? System Context

Visualforce:

Visualforce Pages do enforce CRUD

Why?User Context

11

まず、大前提として・・・

Apex:

Apex は、CRUDもFLSも遵守しません。

システムコンテクストだから。

Visualforce:

Visualforce は、CRUDもFLSも遵守します。

ユーザコンテクストだから。

12

考えるべきポイントは2つ

1. ユーザに対して表示するデータ

2. データに対する変更(作成、更新、削除)

13

考えるべきポイントは2×2

1. ユーザに対して表示するデータ

2. データに対する変更(作成、更新、削除)

14

更に、ここもポイントは2つ。

① Visualforce層を介する場合

② Visualforce層を介さない場合

考えるべきポイントは2×2

1. ユーザに対して表示するデータ

15

更に、ここもポイントは2つ。

① Visualforce層を介するとは?

差込項目が、Sobjectや項目を直接参照

しているケース。

Visualforce Page

01 <apex:page standardController="Account">

02 <apex:pageBlock title="Contacts">

03 <apex:dataTable value="{!account.Contacts}" var="contact" cellPadding="4"

border="1">

04 <apex:column>

05 <apex:facet name="header">Name</apex:facet>

06 {!contact.Name}

07 </apex:column>

08 <apex:column>

09 <apex:facet name="header">Phone</apex:facet>

10 {!contact.Phone}

11 </apex:column>

12 </apex:dataTable>

13 </apex:pageBlock>

14 </apex:page>

16

考えるべきポイントは2×2

1. ユーザに対して表示するデータ

17

更に、ここもポイントは2つ。

① Visualforce層を介する場合

② Visualforce層を介さないとは?

文字列、整数、Apex クラスなどのオブ

ジェクトを介して SObject データを参照

している場合

Visualforce Page

01 <apex:page controller="MyLanguageController" showheader="false"

applyBodyTag="true" sidebar="false">

02 <apex:pageMessages />

03

04 <apex:form >

05 <apex:selectList value="{!selectedLanguage}" multiselect="False" size="1"

style="width:115px;">

06 <apex:selectOptions value="{!languageOptions}"/>

07 </apex:selectList>

08 <apex:commandButton value="change!" action="{!save}" />

09 </apex:form>

10

11 </apex:page>

18

考えるべきポイントは2×2

1. ユーザに対して表示するデータ

19

① Visualforce層を介する場合

⇒CRUD、FLSが自動チェックされる。

② Visualforce層を介さない場合

⇒CRUD、FLSの自動チェックはない。

よって、自前でチェックする必要

がある!

考えるべきポイントは2つ

1. ユーザに対して表示するデータ

2. データに対する変更(作成、更新、削除)

20

考えるべきポイントは2×2

1. ユーザに対して表示するデータ

2. データに対する変更(作成、更新、削除)

21

ここも同様のポイントが2つ。

① Visualforce層を介する場合

② Visualforce層を介さない場合

考えるべきポイントは2×2

2. データに対する変更(作成、更新、削除)

22

① Visualforce層を介する場合

⇒CRUD、FLSが自動チェックされる。

② Visualforce層を介さない場合

⇒CRUD、FLSの自動チェックはない。

よって、自前でチェックする必要

がある!

考えるべきポイントは2×2

1. ユーザに対して表示するデータ

2. データに対する変更(作成、更新、削除)

23

いずれのパターンにおいても、

Visualforce層を介するレンダリングが推

奨される。

考えるべきポイントは2×2

1. ユーザに対して表示するデータ

2. データに対する変更(作成、更新、削除)

24

では、Visualforce層を介さない場合、

どうチェックするのか?

25

#3 どうやって チェックするの~

パターンは4+α

1. データ表示(R)

2. データ作成(C)

3. データ編集(U)

4. データ削除(D)

26

パターンは4+α

1. データ表示(R)

2. データ作成(C)

3. データ編集(U)

4. データ削除(D)

27

Visualforce Page

01 <apex:page controller="RandomContactController">

02 <apex:outputText value="{!getRandomName}" />

03 </apex:page>

28

Apex Controller

01 public with sharing class RandomContactController {

02 public String getGetRandomName() {

03

04 Contact [] myList = [SELECT Name FROM Contact LIMIT 1000];

05 // Pick a list entry at random

06 Integer index = Math.mod(Math.abs(Crypto.getRandomInteger()),myList.size());

07 Contact selected = myList.get(index);

08 return selected.Name;

09 }

10 }

Visualforce Page

01 <apex:page controller="RandomContactController">

02 <apex:outputText value="{!getRandomName}" />

03 </apex:page>

29

Apex Controller

01 public with sharing class RandomContactController {

02 public String getGetRandomName() {

03

04 // Check if the user has read access on the Contact.Name field

05 if (!Schema.sObjectType.Contact.fields.Name.isAccessible()) {

06 return '';

07 }

08

09 Contact [] myList = [SELECT Name FROM Contact LIMIT 1000];

10 // Pick a list entry at random

11 Integer index = Math.mod(Math.abs(Crypto.getRandomInteger()),myList.size());

12 Contact selected = myList.get(index);

13 return selected.Name;

14 }

15 }

パターンは4+α

1. データ表示(R)

2. データ作成(C)

3. データ編集(U)

4. データ削除(D)

30

Visualforce Page

01 <apex:page standardcontroller="Lead" extensions="LeadConverterExtension">

02 <apex:pageMessages />

03 <apex:pageBlock title="Lead">

04 <apex:outputField value="{!Lead.Name}" /><br />

05 <apex:outputField value="{!Lead.Company}" /><br />

06 <apex:outputField value="{!Lead.Phone}" /><br />

07 <apex:form>

08 <apex:commandButton action="{!convertLead}" value="Convert To Contact" />

09 </apex:form>

10 </apex:pageBlock>

11 </apex:page>

31

32

Apex Controller

01 public with sharing class LeadConverterExtension {

02 private Lead l;

03 public LeadConverterExtension(ApexPages.StandardController ctr) {

04 l = [SELECT FirstName,LastName,Phone,Company FROM Lead WHERE Id=:ctr.getRecord().Id];

05 }

06

07 public PageReference convertLead() {

08 Contact c =

new Contact(FirstName = l.FirstName, LastName = l.LastName, Phone = l.Phone);

09 insert c;

10 return null;

11 }

12 }

33

Apex Controller

01 public with sharing class LeadConverterExtension {

02 private Lead l;

03 public LeadConverterExtension(ApexPages.StandardController ctr) {

04 l = [SELECT FirstName,LastName,Phone FROM Lead WHERE Id=:ctr.getRecord().Id];

05 }

06

07 public PageReference convertLead() {

08 String [] contactUpdateFields = new String []

{'FirstName', 'LastName', 'Phone'};

09

10 Map<String,Schema.SObjectField> m = Schema.SObjectType.Contact.fields.getMap();

11 for (String fieldToCheck : contactUpdateFields) {

12 if (!m.get(fieldToCheck).getDescribe().isCreateable()) {

13 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, 'Insufficient access'));

14 return null;

15 }

16 }

17

18 Contact c =

new Contact(FirstName = l.FirstName, LastName = l.LastName, Phone = l.Phone);

19 insert c;

20 return null;

21 }

22 }

パターンは4+α

1. データ表示(R)

2. データ作成(C)

3. データ編集(U)

4. データ削除(D)

34

35

Apex Controller

01 public with sharing class LeadConverterExtension {

02 private Lead l;

03 public LeadConverterExtension(ApexPages.StandardController ctr) {

04 l = [SELECT FirstName,LastName,Phone FROM Lead WHERE Id=:ctr.getRecord().Id];

05 }

06

07 public PageReference convertLead() {

08 String [] contactUpdateFields = new String []

{'FirstName', 'LastName', 'Phone'};

09

10 Map<String,Schema.SObjectField> m = Schema.SObjectType.Contact.fields.getMap();

11 for (String fieldToCheck : contactUpdateFields) {

12 if (!m.get(fieldToCheck).getDescribe().isCreateable()) {

13 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, 'Insufficient access'));

14 return null;

15 }

16 }

17

18 Contact c =

new Contact(FirstName = l.FirstName, LastName = l.LastName, Phone = l.Phone);

19 insert c;

20 return null;

21 }

22 }

isCreateable() を

isUpdateable() に

してチェック!

パターンは4+α

1. データ表示(R)

2. データ作成(C)

3. データ編集(U)

4. データ削除(D)

36

Visualforce Page

01 <apex:page standardcontroller="Lead" extensions="LeadDeleteExtension">

02 <apex:pageMessages />

03 <apex:pageBlock title="Lead">

04 <apex:outputField value="{!Lead.Name}" /><br />

05 <apex:outputField value="{!Lead.Company}" /><br />

06 <apex:outputField value="{!Lead.Phone}" /><br />

07 <apex:form>

08 <apex:commandButton action="{!deleteLead}" value=“Delete" />

09 </apex:form>

10 </apex:pageBlock>

11 </apex:page>

37

38

Apex Controller

01 public with sharing class LeadDeleteExtension {

02 private Lead l;

03 public LeadDeleteExtension(ApexPages.StandardController ctr) {

04 l = [SELECT Id FROM Lead WHERE Id=:ctr.getRecord().Id];

05 }

06

07 public PageReference deleteLead() {

09 delete l;

10 return null;

11 }

12 }

39

Apex Controller

01 public with sharing class LeadDeleteExtension {

02 private Lead l;

03 public LeadDeleteExtension(ApexPages.StandardController ctr) {

04 l = [SELECT Id FROM Lead WHERE Id=:ctr.getRecord().Id];

05 }

06

07 public PageReference deleteLead() {

08 if (!Lead.sObjectType.getDescribe().isDeletable()) {

09 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, 'Insufficient access'));

10 return null;

11 }

12 }

13

14 delete c;

15 return null;

16 }

17 }

isDeletable() は オブジェクトレベルで

チェック!

パターンは4+α

1. データ表示(R)

2. データ作成(C)

3. データ編集(U)

4. データ削除(D)

5. Apexコントローラではなく、

Visualforceページでチェック!

40

Visualforce Page

<!– 表示権限チェック -->

01 <apex:outputText value="{!contactName}"

02 rendered="{!$ObjectType.Contact.fields.Name.Accessible}" />

<!– 作成権限チェック -->

01 <apex:inputText value="{!stringToBecomeNewContactEmail}"

02 rendered="{!$ObjectType.Contact.fields.Email.Createable}" />

<!– 更新権限チェック -->

01 <apex:inputText value="{!contactEmail}"

02 rendered="{!$ObjectType.Contact.fields.Email.Updateable}" />

<!– 削除権限チェック -->

01 <apex:commandButton action="{!CustomDelete}“

02 rendered="{!$ObjectType.Contact.Deletable}" />

41

42

Enjoy Salesforce Developer

Thank You!